xref: /openbmc/skeleton/op-pwrctl/pgood_wait/pgood_wait.c (revision aea53afa3948cc08bdca1a5da3e56f744db624bc)
1 /**
2  * Copyright © 2016 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <errno.h>
17 #include <stdbool.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <systemd/sd-bus.h>
22 #include <systemd/sd-event.h>
23 
24 /* Copied from phosphor-objmgr to make the code compile. */
25 static const int mapper_busy_retries = 5;
26 static const uint64_t mapper_busy_delay_interval_usec = 1000000;
27 
28 static void quit(int r, void *loop)
29 {
30 	sd_event_exit((sd_event *)loop, r);
31 }
32 
33 static int callback(sd_bus_message *m, void *user, sd_bus_error *error)
34 {
35 	(void) error;
36 
37 	sd_event *loop = user;
38 	int r;
39 	char *property = NULL;
40 
41 	r = sd_bus_message_skip(m, "s");
42 	if (r < 0)
43 	{
44 		fprintf(stderr, "Error skipping message fields: %s\n",
45 				strerror(-r));
46 		quit(r, loop);
47 		return r;
48 	}
49 
50 	r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
51 	if (r < 0)
52 	{
53 		fprintf(stderr, "Error entering container: %s\n",
54 				strerror(-r));
55 		quit(r, loop);
56 		return r;
57 	}
58 
59 	while ((r = sd_bus_message_enter_container(
60 				m,
61 				SD_BUS_TYPE_DICT_ENTRY,
62 				"sv")) > 0)
63 	{
64 		r = sd_bus_message_read(m, "s", &property);
65 		if (r < 0)
66 		{
67 			fprintf(stderr, "Error reading message: %s\n",
68 					strerror(-r));
69 			quit(r, loop);
70 			return r;
71 		}
72 
73 		if (strcmp(property, "pgood"))
74 			continue;
75 
76 		quit(0, loop);
77 		break;
78 	}
79 
80 	return 0;
81 }
82 
83 static int get_object(sd_bus *conn, const char *obj,
84 					  sd_bus_message **reply)
85 {
86 	sd_bus_message *request = NULL;
87 	int r, retry = 0;
88 
89 	r = sd_bus_message_new_method_call(
90 		conn, &request, "xyz.openbmc_project.ObjectMapper",
91 		"/xyz/openbmc_project/object_mapper",
92 		"xyz.openbmc_project.ObjectMapper", "GetObject");
93 	if (r < 0)
94 		goto exit;
95 
96 	r = sd_bus_message_append(request, "s", obj);
97 	if (r < 0)
98 		goto exit;
99 	r = sd_bus_message_append(request, "as", 0, NULL);
100 	if (r < 0)
101 		goto exit;
102 
103 	while (true)
104 	{
105 		r = sd_bus_call(conn, request, 0, NULL, reply);
106 		if (r == -EBUSY || r == -ENOBUFS)
107 		{
108 			if (retry >= mapper_busy_retries)
109 				break;
110 
111 			usleep(mapper_busy_delay_interval_usec * (1 << retry));
112 			++retry;
113 			continue;
114 		}
115 		break;
116 	}
117 
118 	if (r < 0)
119 		goto exit;
120 
121 exit:
122 	sd_bus_message_unref(request);
123 
124 	return r;
125 }
126 
127 static int get_service(sd_bus *conn, const char *obj, char **service)
128 {
129 	sd_bus_message *reply = NULL;
130 	const char *tmp;
131 	int r;
132 
133 	r = get_object(conn, obj, &reply);
134 	if (r < 0)
135 		goto exit;
136 
137 	r = sd_bus_message_enter_container(reply, 0, NULL);
138 	if (r < 0)
139 		goto exit;
140 
141 	r = sd_bus_message_enter_container(reply, 0, NULL);
142 	if (r < 0)
143 		goto exit;
144 
145 	r = sd_bus_message_read(reply, "s", &tmp);
146 	if (r < 0)
147 		goto exit;
148 
149 	*service = strdup(tmp);
150 
151 exit:
152 	sd_bus_message_unref(reply);
153 
154 	return r;
155 }
156 
157 int main(int argc, char *argv[])
158 {
159 	static const char *matchfmt =
160 		"type='signal',"
161 		"interface='org.freedesktop.DBus.Properties',"
162 		"member='PropertiesChanged',"
163 		"arg0='org.openbmc.control.Power',"
164 		"path='%s',"
165 		"sender='%s'";
166 	static const char *usage =
167 		"Usage: %s OBJECTPATH on|off\n";
168 	static const size_t LEN = 256;
169 
170 	sd_bus *conn = NULL;
171 	sd_event *loop = NULL;
172 	sd_bus_slot *slot = NULL;
173 	sd_bus_error error = SD_BUS_ERROR_NULL;
174 	char *service = NULL;
175 	int r, dest = -1, state;
176 	char match[LEN];
177 
178 	if (argc < 3)
179 	{
180 		fprintf(stderr, usage, argv[0]);
181 		exit(EXIT_FAILURE);
182 	}
183 
184 	if (!strcmp(argv[2], "on"))
185 		dest = 1;
186 	if (!strcmp(argv[2], "off"))
187 		dest = 0;
188 
189 	if (dest != 0 && dest != 1)
190 	{
191 		fprintf(stderr, usage, argv[0]);
192 		exit(EXIT_FAILURE);
193 	}
194 
195 	r = sd_bus_default_system(&conn);
196 	if (r < 0)
197 	{
198 		fprintf(stderr, "Error connecting to system bus: %s\n",
199 				strerror(-r));
200 		goto finish;
201 	}
202 
203 	r = get_service(conn, argv[1], &service);
204 	if (r < 0)
205 	{
206 		fprintf(stderr, "Error obtaining host service: %s\n",
207 				strerror(-r));
208 		goto finish;
209 	}
210 
211 	r = sd_event_default(&loop);
212 	if (r < 0)
213 	{
214 		fprintf(stderr, "Error obtaining event loop: %s\n",
215 				strerror(-r));
216 		goto finish;
217 	}
218 
219 	r = sd_bus_attach_event(conn, loop, SD_EVENT_PRIORITY_NORMAL);
220 	if (r < 0)
221 	{
222 		fprintf(stderr, "Failed to attach system "
223 						"bus to event loop: %s\n",
224 				strerror(-r));
225 		goto finish;
226 	}
227 
228 	if (strlen(matchfmt) + strnlen(argv[1], LEN) > LEN)
229 	{
230 		r = -E2BIG;
231 		fprintf(stderr, "Error adding match rule: %s\n",
232 				strerror(-r));
233 		goto finish;
234 	}
235 
236 	sprintf(match, matchfmt, argv[1], service);
237 
238 	r = sd_bus_add_match(conn,
239 						 &slot,
240 						 match,
241 						 callback,
242 						 loop);
243 	if (r < 0)
244 	{
245 		fprintf(stderr, "Error adding match rule: %s\n",
246 				strerror(-r));
247 		goto finish;
248 	}
249 
250 	r = sd_bus_get_property_trivial(conn,
251 									service,
252 									argv[1],
253 									"org.openbmc.control.Power",
254 									"pgood",
255 									&error,
256 									'i',
257 									&state);
258 	if (r < 0)
259 	{
260 		fprintf(stderr, "Error getting property: %s\n",
261 				strerror(-r));
262 		goto finish;
263 	}
264 
265 	if (dest == state)
266 		goto finish;
267 
268 	r = sd_event_loop(loop);
269 	if (r < 0)
270 	{
271 		fprintf(stderr, "Error starting event loop: %s\n",
272 				strerror(-r));
273 		goto finish;
274 	}
275 
276 finish:
277 	exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
278 }
279