1 /* 2 * rpcd - UBUS RPC server 3 * 4 * Copyright (C) 2013-2014 Jo-Philipp Wich <jow@openwrt.org> 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <stdbool.h> 20 #include <libubus.h> 21 #include <sys/mman.h> 22 23 #include <rpcd/exec.h> 24 #include <rpcd/plugin.h> 25 #include <rpcd/session.h> 26 #include <sys/reboot.h> 27 28 static const struct rpc_daemon_ops *ops; 29 30 enum { 31 RPC_P_USER, 32 RPC_P_PASSWORD, 33 __RPC_P_MAX 34 }; 35 36 static const struct blobmsg_policy rpc_password_policy[__RPC_P_MAX] = { 37 [RPC_P_USER] = { .name = "user", .type = BLOBMSG_TYPE_STRING }, 38 [RPC_P_PASSWORD] = { .name = "password", .type = BLOBMSG_TYPE_STRING }, 39 }; 40 41 enum { 42 RPC_UPGRADE_KEEP, 43 __RPC_UPGRADE_MAX 44 }; 45 46 static const struct blobmsg_policy rpc_upgrade_policy[__RPC_UPGRADE_MAX] = { 47 [RPC_UPGRADE_KEEP] = { .name = "keep", .type = BLOBMSG_TYPE_BOOL }, 48 }; 49 50 enum { 51 RPC_PACKAGELIST_ALL, 52 __RPC_PACKAGELIST_MAX 53 }; 54 55 static const struct blobmsg_policy rpc_packagelist_policy[__RPC_PACKAGELIST_MAX] = { 56 [RPC_PACKAGELIST_ALL] = { .name = "all", .type = BLOBMSG_TYPE_BOOL }, 57 }; 58 59 static int 60 rpc_errno_status(void) 61 { 62 switch (errno) 63 { 64 case EACCES: 65 return UBUS_STATUS_PERMISSION_DENIED; 66 67 case ENOTDIR: 68 return UBUS_STATUS_INVALID_ARGUMENT; 69 70 case ENOENT: 71 return UBUS_STATUS_NOT_FOUND; 72 73 case EINVAL: 74 return UBUS_STATUS_INVALID_ARGUMENT; 75 76 default: 77 return UBUS_STATUS_UNKNOWN_ERROR; 78 } 79 } 80 81 static int 82 rpc_cgi_password_set(struct ubus_context *ctx, struct ubus_object *obj, 83 struct ubus_request_data *req, const char *method, 84 struct blob_attr *msg) 85 { 86 pid_t pid; 87 int fd, fds[2]; 88 struct stat s; 89 struct blob_attr *tb[__RPC_P_MAX]; 90 ssize_t n; 91 int ret; 92 const char *const passwd = "/bin/passwd"; 93 const struct timespec ts = {0, 100 * 1000 * 1000}; 94 95 blobmsg_parse(rpc_password_policy, __RPC_P_MAX, tb, 96 blob_data(msg), blob_len(msg)); 97 98 if (!tb[RPC_P_USER] || !tb[RPC_P_PASSWORD]) 99 return UBUS_STATUS_INVALID_ARGUMENT; 100 101 if (stat(passwd, &s)) 102 return UBUS_STATUS_NOT_FOUND; 103 104 if (!(s.st_mode & S_IXUSR)) 105 return UBUS_STATUS_PERMISSION_DENIED; 106 107 if (pipe(fds)) 108 return rpc_errno_status(); 109 110 switch ((pid = fork())) 111 { 112 case -1: 113 close(fds[0]); 114 close(fds[1]); 115 return rpc_errno_status(); 116 117 case 0: 118 uloop_done(); 119 120 dup2(fds[0], 0); 121 close(fds[0]); 122 close(fds[1]); 123 124 if ((fd = open("/dev/null", O_RDWR)) > -1) 125 { 126 dup2(fd, 1); 127 dup2(fd, 2); 128 close(fd); 129 } 130 131 ret = chdir("/"); 132 if (ret < 0) 133 return rpc_errno_status(); 134 135 if (execl(passwd, passwd, 136 blobmsg_data(tb[RPC_P_USER]), NULL)) 137 return rpc_errno_status(); 138 139 default: 140 close(fds[0]); 141 142 n = write(fds[1], blobmsg_data(tb[RPC_P_PASSWORD]), 143 blobmsg_data_len(tb[RPC_P_PASSWORD]) - 1); 144 if (n < 0) 145 return rpc_errno_status(); 146 147 n = write(fds[1], "\n", 1); 148 if (n < 0) 149 return rpc_errno_status(); 150 151 nanosleep(&ts, NULL); 152 153 n = write(fds[1], blobmsg_data(tb[RPC_P_PASSWORD]), 154 blobmsg_data_len(tb[RPC_P_PASSWORD]) - 1); 155 if (n < 0) 156 return rpc_errno_status(); 157 n = write(fds[1], "\n", 1); 158 if (n < 0) 159 return rpc_errno_status(); 160 161 close(fds[1]); 162 163 waitpid(pid, NULL, 0); 164 165 return 0; 166 } 167 } 168 169 static bool 170 is_all_or_world(const char *pkg, const char **world) 171 { 172 /* compares null-terminated pkg with non-null-terminated world[i] */ 173 /* e.g., "my_pkg\0" == "my_pkg==1.2.3\n" => true */ 174 175 if (!world) return true; /* handles 'all' case */ 176 177 const char *terminators = "\n@~<>="; /* man 5 apk-world */ 178 179 size_t i, c; 180 const char *item; 181 for (i = 0; *world[i]; i++) { 182 item = world[i]; 183 for (c = 0; pkg[c] == item[c]; c++); 184 if (pkg[c] == '\0' && strchr(terminators, item[c])) 185 return true; 186 } 187 188 return false; 189 } 190 191 static bool 192 is_blank(const char *line) 193 { 194 for (; *line; line++) 195 if (!isspace(*line)) 196 return false; 197 return true; 198 } 199 200 static int 201 rpc_sys_packagelist(struct ubus_context *ctx, struct ubus_object *obj, 202 struct ubus_request_data *req, const char *method, 203 struct blob_attr *msg) 204 { 205 struct blob_attr *tb[__RPC_PACKAGELIST_MAX]; 206 bool all = false; 207 struct blob_buf buf = { 0 }; 208 char line[256], pkg[128], ver[128]; 209 void *tbl; 210 struct stat statbuf; 211 const char **world = NULL; 212 char *world_mmap = NULL; 213 size_t world_mmap_size = 0; 214 215 /* 216 * Status file fields, /usr/lib/opkg/status vs /lib/apk/db/installed 217 * opkg apk 218 * PACKAGE_ABIVERSION "ABIVersion" no equivalent - see BUG, below 219 * PACKAGE_AUTOINSTALLED "Auto-Installed" package listed in 'world', not a db field 220 * PACKAGE_NAME "Package" "P" 221 * PACKAGE_STATUS "Status" package listed in db, not a status value 222 * PACKAGE_VERSION "Version" "V" 223 */ 224 225 blobmsg_parse(rpc_packagelist_policy, __RPC_PACKAGELIST_MAX, tb, 226 blob_data(msg), blob_len(msg)); 227 228 if (tb[RPC_PACKAGELIST_ALL] && blobmsg_get_bool(tb[RPC_PACKAGELIST_ALL])) 229 all = true; 230 231 FILE *f = fopen("/lib/apk/db/installed", "r"); 232 if (!f) 233 return UBUS_STATUS_NOT_FOUND; 234 235 if (!all) { 236 /* We return only those items appearing in 'world' file. */ 237 int world_fd = open("/etc/apk/world", O_RDONLY); 238 if (world_fd == -1) 239 return rpc_errno_status(); 240 241 if (fstat(world_fd, &statbuf) == -1) { 242 close(world_fd); 243 return rpc_errno_status(); 244 } 245 246 world_mmap_size = statbuf.st_size + 1; 247 if (world_mmap_size == 1) { 248 /* 'world' file is malformed: empty */ 249 close(world_fd); 250 return UBUS_STATUS_UNKNOWN_ERROR; 251 } 252 253 world_mmap = (char *)mmap(NULL, world_mmap_size, PROT_READ, MAP_PRIVATE, world_fd, 0); 254 close(world_fd); 255 if (world_mmap == MAP_FAILED) { 256 return rpc_errno_status(); 257 } 258 259 if (world_mmap[world_mmap_size-2] != '\n') { 260 /* 'world' file is malformed: missing final newline */ 261 munmap(world_mmap, world_mmap_size); 262 return UBUS_STATUS_UNKNOWN_ERROR; 263 } 264 265 /* resulting 'world' pointer map looks like this: 266 * nstrs = 2 == count of newlines in mmap 267 * mmap = "pkg1\npkg2=1.2\n\0" 268 * | | | 269 * world[0]-+ | | 270 * world[1]-------+ | 271 * world[2]-----------------+ 272 */ 273 274 size_t istr, nstrs; 275 char *s; 276 for (nstrs = 0, s = world_mmap; s[nstrs]; s[nstrs] == '\n' ? nstrs++ : *s++); 277 278 if (nstrs) { 279 /* extra one in world for NULL sentinel */ 280 world = (const char **)calloc(nstrs+1, sizeof(char *)); 281 world[0] = world_mmap; 282 for (istr = 1, s = world_mmap; *s; s++) { 283 if (*s == '\n') { 284 world[istr] = s + 1; 285 istr++; 286 } 287 } 288 } 289 } 290 291 blob_buf_init(&buf, 0); 292 tbl = blobmsg_open_table(&buf, "packages"); 293 pkg[0] = ver[0] = '\0'; 294 295 while (fgets(line, sizeof(line), f)) { 296 switch (line[0]) { 297 case 'P': 298 if (sscanf(line, "P: %127s", pkg) != 1) 299 pkg[0] = '\0'; 300 break; 301 case 'V': 302 if (sscanf(line, "V: %127s", ver) != 1) 303 ver[0] = '\0'; 304 break; 305 default: 306 if (is_blank(line)) { 307 if (pkg[0] && ver[0] && is_all_or_world(pkg, world)) { 308 /* BUG: There's no ABI version info in any of 309 * the apk files, so some of the returned file 310 * names contain ABI-versioning. 311 * 312 * If you had that information, you'd apply it here. 313 */ 314 blobmsg_add_string(&buf, pkg, ver); 315 } 316 pkg[0] = ver[0] = '\0'; 317 } 318 break; 319 } 320 } 321 322 if (world) 323 free(world); 324 if (world_mmap) 325 munmap(world_mmap, world_mmap_size); 326 327 blobmsg_close_table(&buf, tbl); 328 ubus_send_reply(ctx, req, buf.head); 329 blob_buf_free(&buf); 330 fclose(f); 331 332 return 0; 333 } 334 335 static int 336 rpc_sys_upgrade_test(struct ubus_context *ctx, struct ubus_object *obj, 337 struct ubus_request_data *req, const char *method, 338 struct blob_attr *msg) 339 { 340 const char *cmd[4] = { "sysupgrade", "--test", "/tmp/firmware.bin", NULL }; 341 return ops->exec(cmd, NULL, NULL, NULL, NULL, NULL, ctx, req); 342 } 343 344 static int 345 rpc_sys_upgrade_start(struct ubus_context *ctx, struct ubus_object *obj, 346 struct ubus_request_data *req, const char *method, 347 struct blob_attr *msg) 348 { 349 struct blob_attr *tb[__RPC_UPGRADE_MAX]; 350 char * const cmd[4] = { "/sbin/sysupgrade", "-n", "/tmp/firmware.bin", NULL }; 351 char * const cmd_keep[3] = { "/sbin/sysupgrade", "/tmp/firmware.bin", NULL }; 352 char * const * c = cmd; 353 354 blobmsg_parse(rpc_upgrade_policy, __RPC_UPGRADE_MAX, tb, 355 blob_data(msg), blob_len(msg)); 356 357 if (tb[RPC_UPGRADE_KEEP] && blobmsg_get_bool(tb[RPC_UPGRADE_KEEP])) 358 c = cmd_keep; 359 360 if (!fork()) { 361 /* wait for the RPC call to complete */ 362 sleep(2); 363 return execv(c[0], c); 364 } 365 366 return 0; 367 } 368 369 static int 370 rpc_sys_upgrade_clean(struct ubus_context *ctx, struct ubus_object *obj, 371 struct ubus_request_data *req, const char *method, 372 struct blob_attr *msg) 373 { 374 if (unlink("/tmp/firmware.bin")) 375 return rpc_errno_status(); 376 377 return 0; 378 } 379 380 static int 381 rpc_sys_factory(struct ubus_context *ctx, struct ubus_object *obj, 382 struct ubus_request_data *req, const char *method, 383 struct blob_attr *msg) 384 { 385 char * const cmd[4] = { "/sbin/jffs2reset", "-y", "-r", NULL }; 386 387 if (!fork()) { 388 /* wait for the RPC call to complete */ 389 sleep(2); 390 return execv(cmd[0], cmd); 391 } 392 393 return 0; 394 } 395 396 static int 397 rpc_sys_reboot(struct ubus_context *ctx, struct ubus_object *obj, 398 struct ubus_request_data *req, const char *method, 399 struct blob_attr *msg) 400 { 401 if (!fork()) { 402 sync(); 403 sleep(2); 404 reboot(RB_AUTOBOOT); 405 while (1) 406 ; 407 } 408 409 return 0; 410 } 411 412 static int 413 rpc_sys_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx) 414 { 415 static const struct ubus_method sys_methods[] = { 416 UBUS_METHOD("packagelist", rpc_sys_packagelist, rpc_packagelist_policy), 417 UBUS_METHOD("password_set", rpc_cgi_password_set, rpc_password_policy), 418 UBUS_METHOD_NOARG("upgrade_test", rpc_sys_upgrade_test), 419 UBUS_METHOD("upgrade_start", rpc_sys_upgrade_start, 420 rpc_upgrade_policy), 421 UBUS_METHOD_NOARG("upgrade_clean", rpc_sys_upgrade_clean), 422 UBUS_METHOD_NOARG("factory", rpc_sys_factory), 423 UBUS_METHOD_NOARG("reboot", rpc_sys_reboot), 424 }; 425 426 static struct ubus_object_type sys_type = 427 UBUS_OBJECT_TYPE("rpcd-plugin-sys", sys_methods); 428 429 static struct ubus_object obj = { 430 .name = "rpc-sys", 431 .type = &sys_type, 432 .methods = sys_methods, 433 .n_methods = ARRAY_SIZE(sys_methods), 434 }; 435 436 ops = o; 437 438 return ubus_add_object(ctx, &obj); 439 } 440 441 struct rpc_plugin rpc_plugin = { 442 .init = rpc_sys_api_init 443 }; 444
This page was automatically generated by LXR 0.3.1. • OpenWrt