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