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