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