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], abi[128], 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 char *token = NULL; 215 216 /* 217 * Status file fields, /usr/lib/opkg/status vs /lib/apk/db/installed 218 * opkg apk 219 * PACKAGE_ABIVERSION "ABIVersion" "g:openwrt:abiversion=" 220 * PACKAGE_AUTOINSTALLED "Auto-Installed" package listed in 'world', not a db field 221 * PACKAGE_NAME "Package" "P" 222 * PACKAGE_STATUS "Status" package listed in db, not a status value 223 * PACKAGE_VERSION "Version" "V" 224 */ 225 226 blobmsg_parse(rpc_packagelist_policy, __RPC_PACKAGELIST_MAX, tb, 227 blob_data(msg), blob_len(msg)); 228 229 if (tb[RPC_PACKAGELIST_ALL] && blobmsg_get_bool(tb[RPC_PACKAGELIST_ALL])) 230 all = true; 231 232 FILE *f = fopen("/lib/apk/db/installed", "r"); 233 if (!f) 234 return UBUS_STATUS_NOT_FOUND; 235 236 if (!all) { 237 /* We return only those items appearing in 'world' file. */ 238 int world_fd = open("/etc/apk/world", O_RDONLY); 239 if (world_fd == -1) 240 return rpc_errno_status(); 241 242 if (fstat(world_fd, &statbuf) == -1) { 243 close(world_fd); 244 return rpc_errno_status(); 245 } 246 247 world_mmap_size = statbuf.st_size + 1; 248 if (world_mmap_size == 1) { 249 /* 'world' file is malformed: empty */ 250 close(world_fd); 251 return UBUS_STATUS_UNKNOWN_ERROR; 252 } 253 254 world_mmap = (char *)mmap(NULL, world_mmap_size, PROT_READ, MAP_PRIVATE, world_fd, 0); 255 close(world_fd); 256 if (world_mmap == MAP_FAILED) { 257 return rpc_errno_status(); 258 } 259 260 if (world_mmap[world_mmap_size-2] != '\n') { 261 /* 'world' file is malformed: missing final newline */ 262 munmap(world_mmap, world_mmap_size); 263 return UBUS_STATUS_UNKNOWN_ERROR; 264 } 265 266 /* resulting 'world' pointer map looks like this: 267 * nstrs = 2 == count of newlines in mmap 268 * mmap = "pkg1\npkg2=1.2\n\0" 269 * | | | 270 * world[0]-+ | | 271 * world[1]-------+ | 272 * world[2]-----------------+ 273 */ 274 275 size_t istr, nstrs; 276 char *s; 277 for (nstrs = 0, s = world_mmap; s[nstrs]; s[nstrs] == '\n' ? nstrs++ : *s++); 278 279 if (nstrs) { 280 /* extra one in world for NULL sentinel */ 281 world = (const char **)calloc(nstrs+1, sizeof(char *)); 282 world[0] = world_mmap; 283 for (istr = 1, s = world_mmap; *s; s++) { 284 if (*s == '\n') { 285 world[istr] = s + 1; 286 istr++; 287 } 288 } 289 } 290 } 291 292 blob_buf_init(&buf, 0); 293 tbl = blobmsg_open_table(&buf, "packages"); 294 abi[0] = pkg[0] = ver[0] = '\0'; 295 296 while (fgets(line, sizeof(line), f)) { 297 switch (line[0]) { 298 case 'P': 299 if (sscanf(line, "P: %127s", pkg) != 1) 300 pkg[0] = '\0'; 301 break; 302 case 'V': 303 if (sscanf(line, "V: %127s", ver) != 1) 304 ver[0] = '\0'; 305 break; 306 case 'g': 307 /* this is a custom tag, defined in include/package-pack.mk */ 308 token = strtok(line+2, " =\n"); 309 while (token) { 310 if (strcmp(token, "openwrt:abiversion") == 0) { 311 token = strtok(NULL, " =\n"); 312 if (token) 313 strlcpy(abi, token, sizeof(abi)); 314 break; 315 } 316 token = strtok(NULL, " =\n"); 317 } 318 break; 319 default: 320 if (is_blank(line)) { 321 if (pkg[0] && ver[0] && is_all_or_world(pkg, world)) { 322 if (abi[0]) 323 pkg[strlen(pkg)-strlen(abi)] = '\0'; 324 blobmsg_add_string(&buf, pkg, ver); 325 } 326 abi[0] = pkg[0] = ver[0] = '\0'; 327 } 328 break; 329 } 330 } 331 332 if (world) 333 free(world); 334 if (world_mmap) 335 munmap(world_mmap, world_mmap_size); 336 337 blobmsg_close_table(&buf, tbl); 338 ubus_send_reply(ctx, req, buf.head); 339 blob_buf_free(&buf); 340 fclose(f); 341 342 return 0; 343 } 344 345 static int 346 rpc_sys_upgrade_test(struct ubus_context *ctx, struct ubus_object *obj, 347 struct ubus_request_data *req, const char *method, 348 struct blob_attr *msg) 349 { 350 const char *cmd[4] = { "sysupgrade", "--test", "/tmp/firmware.bin", NULL }; 351 return ops->exec(cmd, NULL, NULL, NULL, NULL, NULL, ctx, req); 352 } 353 354 static int 355 rpc_sys_upgrade_start(struct ubus_context *ctx, struct ubus_object *obj, 356 struct ubus_request_data *req, const char *method, 357 struct blob_attr *msg) 358 { 359 struct blob_attr *tb[__RPC_UPGRADE_MAX]; 360 char * const cmd[4] = { "/sbin/sysupgrade", "-n", "/tmp/firmware.bin", NULL }; 361 char * const cmd_keep[3] = { "/sbin/sysupgrade", "/tmp/firmware.bin", NULL }; 362 char * const * c = cmd; 363 364 blobmsg_parse(rpc_upgrade_policy, __RPC_UPGRADE_MAX, tb, 365 blob_data(msg), blob_len(msg)); 366 367 if (tb[RPC_UPGRADE_KEEP] && blobmsg_get_bool(tb[RPC_UPGRADE_KEEP])) 368 c = cmd_keep; 369 370 if (!fork()) { 371 /* wait for the RPC call to complete */ 372 sleep(2); 373 return execv(c[0], c); 374 } 375 376 return 0; 377 } 378 379 static int 380 rpc_sys_upgrade_clean(struct ubus_context *ctx, struct ubus_object *obj, 381 struct ubus_request_data *req, const char *method, 382 struct blob_attr *msg) 383 { 384 if (unlink("/tmp/firmware.bin")) 385 return rpc_errno_status(); 386 387 return 0; 388 } 389 390 static int 391 rpc_sys_factory(struct ubus_context *ctx, struct ubus_object *obj, 392 struct ubus_request_data *req, const char *method, 393 struct blob_attr *msg) 394 { 395 char * const cmd[4] = { "/sbin/jffs2reset", "-y", "-r", NULL }; 396 397 if (!fork()) { 398 /* wait for the RPC call to complete */ 399 sleep(2); 400 return execv(cmd[0], cmd); 401 } 402 403 return 0; 404 } 405 406 static int 407 rpc_sys_reboot(struct ubus_context *ctx, struct ubus_object *obj, 408 struct ubus_request_data *req, const char *method, 409 struct blob_attr *msg) 410 { 411 if (!fork()) { 412 sync(); 413 sleep(2); 414 reboot(RB_AUTOBOOT); 415 while (1) 416 ; 417 } 418 419 return 0; 420 } 421 422 static int 423 rpc_sys_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx) 424 { 425 static const struct ubus_method sys_methods[] = { 426 UBUS_METHOD("packagelist", rpc_sys_packagelist, rpc_packagelist_policy), 427 UBUS_METHOD("password_set", rpc_cgi_password_set, rpc_password_policy), 428 UBUS_METHOD_NOARG("upgrade_test", rpc_sys_upgrade_test), 429 UBUS_METHOD("upgrade_start", rpc_sys_upgrade_start, 430 rpc_upgrade_policy), 431 UBUS_METHOD_NOARG("upgrade_clean", rpc_sys_upgrade_clean), 432 UBUS_METHOD_NOARG("factory", rpc_sys_factory), 433 UBUS_METHOD_NOARG("reboot", rpc_sys_reboot), 434 }; 435 436 static struct ubus_object_type sys_type = 437 UBUS_OBJECT_TYPE("rpcd-plugin-sys", sys_methods); 438 439 static struct ubus_object obj = { 440 .name = "rpc-sys", 441 .type = &sys_type, 442 .methods = sys_methods, 443 .n_methods = ARRAY_SIZE(sys_methods), 444 }; 445 446 ops = o; 447 448 return ubus_add_object(ctx, &obj); 449 } 450 451 struct rpc_plugin rpc_plugin = { 452 .init = rpc_sys_api_init 453 }; 454
This page was automatically generated by LXR 0.3.1. • OpenWrt