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 22 #include <rpcd/exec.h> 23 #include <rpcd/plugin.h> 24 #include <rpcd/session.h> 25 #include <sys/reboot.h> 26 27 static const struct rpc_daemon_ops *ops; 28 29 enum { 30 RPC_P_USER, 31 RPC_P_PASSWORD, 32 __RPC_P_MAX 33 }; 34 35 static const struct blobmsg_policy rpc_password_policy[__RPC_P_MAX] = { 36 [RPC_P_USER] = { .name = "user", .type = BLOBMSG_TYPE_STRING }, 37 [RPC_P_PASSWORD] = { .name = "password", .type = BLOBMSG_TYPE_STRING }, 38 }; 39 40 enum { 41 RPC_UPGRADE_KEEP, 42 __RPC_UPGRADE_MAX 43 }; 44 45 static const struct blobmsg_policy rpc_upgrade_policy[__RPC_UPGRADE_MAX] = { 46 [RPC_UPGRADE_KEEP] = { .name = "keep", .type = BLOBMSG_TYPE_BOOL }, 47 }; 48 49 enum { 50 RPC_PACKAGELIST_ALL, 51 __RPC_PACKAGELIST_MAX 52 }; 53 54 static const struct blobmsg_policy rpc_packagelist_policy[__RPC_PACKAGELIST_MAX] = { 55 [RPC_PACKAGELIST_ALL] = { .name = "all", .type = BLOBMSG_TYPE_BOOL }, 56 }; 57 58 static int 59 rpc_errno_status(void) 60 { 61 switch (errno) 62 { 63 case EACCES: 64 return UBUS_STATUS_PERMISSION_DENIED; 65 66 case ENOTDIR: 67 return UBUS_STATUS_INVALID_ARGUMENT; 68 69 case ENOENT: 70 return UBUS_STATUS_NOT_FOUND; 71 72 case EINVAL: 73 return UBUS_STATUS_INVALID_ARGUMENT; 74 75 default: 76 return UBUS_STATUS_UNKNOWN_ERROR; 77 } 78 } 79 80 static int 81 rpc_cgi_password_set(struct ubus_context *ctx, struct ubus_object *obj, 82 struct ubus_request_data *req, const char *method, 83 struct blob_attr *msg) 84 { 85 pid_t pid; 86 int fd, fds[2]; 87 struct stat s; 88 struct blob_attr *tb[__RPC_P_MAX]; 89 ssize_t n; 90 int ret; 91 const char *const passwd = "/bin/passwd"; 92 const struct timespec ts = {0, 100 * 1000 * 1000}; 93 94 blobmsg_parse(rpc_password_policy, __RPC_P_MAX, tb, 95 blob_data(msg), blob_len(msg)); 96 97 if (!tb[RPC_P_USER] || !tb[RPC_P_PASSWORD]) 98 return UBUS_STATUS_INVALID_ARGUMENT; 99 100 if (stat(passwd, &s)) 101 return UBUS_STATUS_NOT_FOUND; 102 103 if (!(s.st_mode & S_IXUSR)) 104 return UBUS_STATUS_PERMISSION_DENIED; 105 106 if (pipe(fds)) 107 return rpc_errno_status(); 108 109 switch ((pid = fork())) 110 { 111 case -1: 112 close(fds[0]); 113 close(fds[1]); 114 return rpc_errno_status(); 115 116 case 0: 117 uloop_done(); 118 119 dup2(fds[0], 0); 120 close(fds[0]); 121 close(fds[1]); 122 123 if ((fd = open("/dev/null", O_RDWR)) > -1) 124 { 125 dup2(fd, 1); 126 dup2(fd, 2); 127 close(fd); 128 } 129 130 ret = chdir("/"); 131 if (ret < 0) 132 return rpc_errno_status(); 133 134 if (execl(passwd, passwd, 135 blobmsg_data(tb[RPC_P_USER]), NULL)) 136 return rpc_errno_status(); 137 138 default: 139 close(fds[0]); 140 141 n = write(fds[1], blobmsg_data(tb[RPC_P_PASSWORD]), 142 blobmsg_data_len(tb[RPC_P_PASSWORD]) - 1); 143 if (n < 0) 144 return rpc_errno_status(); 145 146 n = write(fds[1], "\n", 1); 147 if (n < 0) 148 return rpc_errno_status(); 149 150 nanosleep(&ts, NULL); 151 152 n = write(fds[1], blobmsg_data(tb[RPC_P_PASSWORD]), 153 blobmsg_data_len(tb[RPC_P_PASSWORD]) - 1); 154 if (n < 0) 155 return rpc_errno_status(); 156 n = write(fds[1], "\n", 1); 157 if (n < 0) 158 return rpc_errno_status(); 159 160 close(fds[1]); 161 162 waitpid(pid, NULL, 0); 163 164 return 0; 165 } 166 } 167 168 static bool 169 is_field(const char *type, const char *line) 170 { 171 return strncmp(line, type, strlen(type)) == 0; 172 } 173 174 static bool 175 is_blank(const char *line) 176 { 177 for (; *line; line++) 178 if (!isspace(*line)) 179 return false; 180 return true; 181 } 182 183 static int 184 rpc_sys_packagelist(struct ubus_context *ctx, struct ubus_object *obj, 185 struct ubus_request_data *req, const char *method, 186 struct blob_attr *msg) 187 { 188 struct blob_attr *tb[__RPC_PACKAGELIST_MAX]; 189 bool all = false, installed = false, auto_installed = false; 190 struct blob_buf buf = { 0 }; 191 char line[256], tmp[128], pkg[128], ver[128]; 192 char *pkg_abi; 193 void *tbl; 194 195 blobmsg_parse(rpc_packagelist_policy, __RPC_PACKAGELIST_MAX, tb, 196 blob_data(msg), blob_len(msg)); 197 198 if (tb[RPC_PACKAGELIST_ALL] && blobmsg_get_bool(tb[RPC_PACKAGELIST_ALL])) 199 all = true; 200 201 FILE *f = fopen("/usr/lib/opkg/status", "r"); 202 if (!f) 203 return UBUS_STATUS_NOT_FOUND; 204 205 blob_buf_init(&buf, 0); 206 tbl = blobmsg_open_table(&buf, "packages"); 207 pkg[0] = ver[0] = '\0'; 208 209 while (fgets(line, sizeof(line), f)) { 210 switch (line[0]) { 211 case 'A': 212 if (is_field("ABIVersion", line)) { 213 /* if there is ABIVersion, remove that suffix */ 214 if (sscanf(line, "ABIVersion: %127s", tmp) == 1 215 && strlen(tmp) < strlen(pkg)) { 216 pkg_abi = pkg + (strlen(pkg) - strlen(tmp)); 217 if (strncmp(pkg_abi, tmp, strlen(tmp)) == 0) 218 pkg_abi[0] = '\0'; 219 } 220 } else if (is_field("Auto-Installed", line)) 221 if (sscanf(line, "Auto-Installed: %63s", tmp) == 1) 222 auto_installed = (strcmp(tmp, "yes") == 0); 223 break; 224 case 'P': 225 if (is_field("Package", line)) 226 if (sscanf(line, "Package: %127s", pkg) != 1) 227 pkg[0] = '\0'; 228 break; 229 case 'V': 230 if (is_field("Version", line)) 231 if (sscanf(line, "Version: %127s", ver) != 1) 232 ver[0] = '\0'; 233 break; 234 case 'S': 235 if (is_field("Status", line)) 236 if (sscanf(line, "Status: install %63s installed", tmp) == 1) 237 installed = true; 238 break; 239 default: 240 if (is_blank(line)) { 241 if (installed && (all || !auto_installed) && pkg[0] && ver[0]) 242 blobmsg_add_string(&buf, pkg, ver); 243 pkg[0] = ver[0] = '\0'; 244 installed = auto_installed = false; 245 } 246 break; 247 } 248 } 249 250 blobmsg_close_table(&buf, tbl); 251 ubus_send_reply(ctx, req, buf.head); 252 blob_buf_free(&buf); 253 fclose(f); 254 255 return 0; 256 } 257 258 static int 259 rpc_sys_upgrade_test(struct ubus_context *ctx, struct ubus_object *obj, 260 struct ubus_request_data *req, const char *method, 261 struct blob_attr *msg) 262 { 263 const char *cmd[4] = { "sysupgrade", "--test", "/tmp/firmware.bin", NULL }; 264 return ops->exec(cmd, NULL, NULL, NULL, NULL, NULL, ctx, req); 265 } 266 267 static int 268 rpc_sys_upgrade_start(struct ubus_context *ctx, struct ubus_object *obj, 269 struct ubus_request_data *req, const char *method, 270 struct blob_attr *msg) 271 { 272 struct blob_attr *tb[__RPC_UPGRADE_MAX]; 273 char * const cmd[4] = { "/sbin/sysupgrade", "-n", "/tmp/firmware.bin", NULL }; 274 char * const cmd_keep[3] = { "/sbin/sysupgrade", "/tmp/firmware.bin", NULL }; 275 char * const * c = cmd; 276 277 blobmsg_parse(rpc_upgrade_policy, __RPC_UPGRADE_MAX, tb, 278 blob_data(msg), blob_len(msg)); 279 280 if (tb[RPC_UPGRADE_KEEP] && blobmsg_get_bool(tb[RPC_UPGRADE_KEEP])) 281 c = cmd_keep; 282 283 if (!fork()) { 284 /* wait for the RPC call to complete */ 285 sleep(2); 286 return execv(c[0], c); 287 } 288 289 return 0; 290 } 291 292 static int 293 rpc_sys_upgrade_clean(struct ubus_context *ctx, struct ubus_object *obj, 294 struct ubus_request_data *req, const char *method, 295 struct blob_attr *msg) 296 { 297 if (unlink("/tmp/firmware.bin")) 298 return rpc_errno_status(); 299 300 return 0; 301 } 302 303 static int 304 rpc_sys_factory(struct ubus_context *ctx, struct ubus_object *obj, 305 struct ubus_request_data *req, const char *method, 306 struct blob_attr *msg) 307 { 308 char * const cmd[4] = { "/sbin/jffs2reset", "-y", "-r", NULL }; 309 310 if (!fork()) { 311 /* wait for the RPC call to complete */ 312 sleep(2); 313 return execv(cmd[0], cmd); 314 } 315 316 return 0; 317 } 318 319 static int 320 rpc_sys_reboot(struct ubus_context *ctx, struct ubus_object *obj, 321 struct ubus_request_data *req, const char *method, 322 struct blob_attr *msg) 323 { 324 if (!fork()) { 325 sync(); 326 sleep(2); 327 reboot(RB_AUTOBOOT); 328 while (1) 329 ; 330 } 331 332 return 0; 333 } 334 335 static int 336 rpc_sys_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx) 337 { 338 static const struct ubus_method sys_methods[] = { 339 UBUS_METHOD("packagelist", rpc_sys_packagelist, rpc_packagelist_policy), 340 UBUS_METHOD("password_set", rpc_cgi_password_set, rpc_password_policy), 341 UBUS_METHOD_NOARG("upgrade_test", rpc_sys_upgrade_test), 342 UBUS_METHOD("upgrade_start", rpc_sys_upgrade_start, 343 rpc_upgrade_policy), 344 UBUS_METHOD_NOARG("upgrade_clean", rpc_sys_upgrade_clean), 345 UBUS_METHOD_NOARG("factory", rpc_sys_factory), 346 UBUS_METHOD_NOARG("reboot", rpc_sys_reboot), 347 }; 348 349 static struct ubus_object_type sys_type = 350 UBUS_OBJECT_TYPE("rpcd-plugin-sys", sys_methods); 351 352 static struct ubus_object obj = { 353 .name = "rpc-sys", 354 .type = &sys_type, 355 .methods = sys_methods, 356 .n_methods = ARRAY_SIZE(sys_methods), 357 }; 358 359 ops = o; 360 361 return ubus_add_object(ctx, &obj); 362 } 363 364 struct rpc_plugin rpc_plugin = { 365 .init = rpc_sys_api_init 366 }; 367
This page was automatically generated by LXR 0.3.1. • OpenWrt