builtins.c revision 8c92ba1921fc8dc3fc7cc39ef854e9ee70fafc67
1/* 2 * Copyright (C) 2008 The Android Open Source Project 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 17#include <sys/types.h> 18#include <sys/stat.h> 19#include <fcntl.h> 20#include <unistd.h> 21#include <string.h> 22#include <stdio.h> 23#include <linux/kd.h> 24#include <errno.h> 25#include <sys/socket.h> 26#include <netinet/in.h> 27#include <linux/if.h> 28#include <arpa/inet.h> 29#include <stdlib.h> 30#include <sys/mount.h> 31#include <sys/resource.h> 32#include <linux/loop.h> 33 34#include "init.h" 35#include "keywords.h" 36#include "property_service.h" 37#include "devices.h" 38 39#include <private/android_filesystem_config.h> 40 41void add_environment(const char *name, const char *value); 42 43extern int init_module(void *, unsigned long, const char *); 44 45static int write_file(const char *path, const char *value) 46{ 47 int fd, ret, len; 48 49 fd = open(path, O_WRONLY|O_CREAT, 0622); 50 51 if (fd < 0) 52 return -errno; 53 54 len = strlen(value); 55 56 do { 57 ret = write(fd, value, len); 58 } while (ret < 0 && errno == EINTR); 59 60 close(fd); 61 if (ret < 0) { 62 return -errno; 63 } else { 64 return 0; 65 } 66} 67 68static int insmod(const char *filename, char *options) 69{ 70 void *module; 71 unsigned size; 72 int ret; 73 74 module = read_file(filename, &size); 75 if (!module) 76 return -1; 77 78 ret = init_module(module, size, options); 79 80 free(module); 81 82 return ret; 83} 84 85static int setkey(struct kbentry *kbe) 86{ 87 int fd, ret; 88 89 fd = open("/dev/tty0", O_RDWR | O_SYNC); 90 if (fd < 0) 91 return -1; 92 93 ret = ioctl(fd, KDSKBENT, kbe); 94 95 close(fd); 96 return ret; 97} 98 99static int __ifupdown(const char *interface, int up) 100{ 101 struct ifreq ifr; 102 int s, ret; 103 104 strlcpy(ifr.ifr_name, interface, IFNAMSIZ); 105 106 s = socket(AF_INET, SOCK_DGRAM, 0); 107 if (s < 0) 108 return -1; 109 110 ret = ioctl(s, SIOCGIFFLAGS, &ifr); 111 if (ret < 0) { 112 goto done; 113 } 114 115 if (up) 116 ifr.ifr_flags |= IFF_UP; 117 else 118 ifr.ifr_flags &= ~IFF_UP; 119 120 ret = ioctl(s, SIOCSIFFLAGS, &ifr); 121 122done: 123 close(s); 124 return ret; 125} 126 127static void service_start_if_not_disabled(struct service *svc) 128{ 129 if (!(svc->flags & SVC_DISABLED)) { 130 service_start(svc, NULL); 131 } 132} 133 134int do_chdir(int nargs, char **args) 135{ 136 chdir(args[1]); 137 return 0; 138} 139 140int do_chroot(int nargs, char **args) 141{ 142 chroot(args[1]); 143 return 0; 144} 145 146int do_class_start(int nargs, char **args) 147{ 148 /* Starting a class does not start services 149 * which are explicitly disabled. They must 150 * be started individually. 151 */ 152 service_for_each_class(args[1], service_start_if_not_disabled); 153 return 0; 154} 155 156int do_class_stop(int nargs, char **args) 157{ 158 service_for_each_class(args[1], service_stop); 159 return 0; 160} 161 162int do_domainname(int nargs, char **args) 163{ 164 return write_file("/proc/sys/kernel/domainname", args[1]); 165} 166 167int do_exec(int nargs, char **args) 168{ 169 return -1; 170} 171 172int do_export(int nargs, char **args) 173{ 174 add_environment(args[1], args[2]); 175 return 0; 176} 177 178int do_hostname(int nargs, char **args) 179{ 180 return write_file("/proc/sys/kernel/hostname", args[1]); 181} 182 183int do_ifup(int nargs, char **args) 184{ 185 return __ifupdown(args[1], 1); 186} 187 188 189static int do_insmod_inner(int nargs, char **args, int opt_len) 190{ 191 char options[opt_len + 1]; 192 int i; 193 194 options[0] = '\0'; 195 if (nargs > 2) { 196 strcpy(options, args[2]); 197 for (i = 3; i < nargs; ++i) { 198 strcat(options, " "); 199 strcat(options, args[i]); 200 } 201 } 202 203 return insmod(args[1], options); 204} 205 206int do_insmod(int nargs, char **args) 207{ 208 int i; 209 int size = 0; 210 211 if (nargs > 2) { 212 for (i = 2; i < nargs; ++i) 213 size += strlen(args[i]) + 1; 214 } 215 216 return do_insmod_inner(nargs, args, size); 217} 218 219int do_import(int nargs, char **args) 220{ 221 return parse_config_file(args[1]); 222} 223 224int do_mkdir(int nargs, char **args) 225{ 226 mode_t mode = 0755; 227 228 /* mkdir <path> [mode] [owner] [group] */ 229 230 if (nargs >= 3) { 231 mode = strtoul(args[2], 0, 8); 232 } 233 234 if (mkdir(args[1], mode)) { 235 return -errno; 236 } 237 238 if (nargs >= 4) { 239 uid_t uid = decode_uid(args[3]); 240 gid_t gid = -1; 241 242 if (nargs == 5) { 243 gid = decode_uid(args[4]); 244 } 245 246 if (chown(args[1], uid, gid)) { 247 return -errno; 248 } 249 } 250 251 return 0; 252} 253 254static struct { 255 const char *name; 256 unsigned flag; 257} mount_flags[] = { 258 { "noatime", MS_NOATIME }, 259 { "nosuid", MS_NOSUID }, 260 { "nodev", MS_NODEV }, 261 { "nodiratime", MS_NODIRATIME }, 262 { "ro", MS_RDONLY }, 263 { "rw", 0 }, 264 { "remount", MS_REMOUNT }, 265 { "defaults", 0 }, 266 { 0, 0 }, 267}; 268 269/* mount <type> <device> <path> <flags ...> <options> */ 270int do_mount(int nargs, char **args) 271{ 272 char tmp[64]; 273 char *source, *target, *system; 274 char *options = NULL; 275 unsigned flags = 0; 276 int n, i; 277 278 for (n = 4; n < nargs; n++) { 279 for (i = 0; mount_flags[i].name; i++) { 280 if (!strcmp(args[n], mount_flags[i].name)) { 281 flags |= mount_flags[i].flag; 282 break; 283 } 284 } 285 286 /* if our last argument isn't a flag, wolf it up as an option string */ 287 if (n + 1 == nargs && !mount_flags[i].name) 288 options = args[n]; 289 } 290 291 system = args[1]; 292 source = args[2]; 293 target = args[3]; 294 295 if (!strncmp(source, "mtd@", 4)) { 296 n = mtd_name_to_number(source + 4); 297 if (n < 0) { 298 return -1; 299 } 300 301 sprintf(tmp, "/dev/block/mtdblock%d", n); 302 303 if (mount(tmp, target, system, flags, options) < 0) { 304 return -1; 305 } 306 307 return 0; 308 } else if (!strncmp(source, "loop@", 5)) { 309 int mode, loop, fd; 310 struct loop_info info; 311 312 mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR; 313 fd = open(source + 5, mode); 314 if (fd < 0) { 315 return -1; 316 } 317 318 for (n = 0; ; n++) { 319 sprintf(tmp, "/dev/block/loop%d", n); 320 loop = open(tmp, mode); 321 if (loop < 0) { 322 return -1; 323 } 324 325 /* if it is a blank loop device */ 326 if (ioctl(loop, LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) { 327 /* if it becomes our loop device */ 328 if (ioctl(loop, LOOP_SET_FD, fd) >= 0) { 329 close(fd); 330 331 if (mount(tmp, target, system, flags, options) < 0) { 332 ioctl(loop, LOOP_CLR_FD, 0); 333 close(loop); 334 return -1; 335 } 336 337 close(loop); 338 return 0; 339 } 340 } 341 342 close(loop); 343 } 344 345 close(fd); 346 ERROR("out of loopback devices"); 347 return -1; 348 } else { 349 if (mount(source, target, system, flags, options) < 0) { 350 return -1; 351 } 352 353 return 0; 354 } 355} 356 357int do_setkey(int nargs, char **args) 358{ 359 struct kbentry kbe; 360 kbe.kb_table = strtoul(args[1], 0, 0); 361 kbe.kb_index = strtoul(args[2], 0, 0); 362 kbe.kb_value = strtoul(args[3], 0, 0); 363 return setkey(&kbe); 364} 365 366int do_setprop(int nargs, char **args) 367{ 368 property_set(args[1], args[2]); 369 return 0; 370} 371 372int do_setrlimit(int nargs, char **args) 373{ 374 struct rlimit limit; 375 int resource; 376 resource = atoi(args[1]); 377 limit.rlim_cur = atoi(args[2]); 378 limit.rlim_max = atoi(args[3]); 379 return setrlimit(resource, &limit); 380} 381 382int do_start(int nargs, char **args) 383{ 384 struct service *svc; 385 svc = service_find_by_name(args[1]); 386 if (svc) { 387 service_start(svc, NULL); 388 } 389 return 0; 390} 391 392int do_stop(int nargs, char **args) 393{ 394 struct service *svc; 395 svc = service_find_by_name(args[1]); 396 if (svc) { 397 service_stop(svc); 398 } 399 return 0; 400} 401 402int do_restart(int nargs, char **args) 403{ 404 struct service *svc; 405 svc = service_find_by_name(args[1]); 406 if (svc) { 407 service_stop(svc); 408 service_start(svc, NULL); 409 } 410 return 0; 411} 412 413int do_trigger(int nargs, char **args) 414{ 415 action_for_each_trigger(args[1], action_add_queue_tail); 416 drain_action_queue(); 417 return 0; 418} 419 420int do_symlink(int nargs, char **args) 421{ 422 return symlink(args[1], args[2]); 423} 424 425int do_sysclktz(int nargs, char **args) 426{ 427 struct timezone tz; 428 429 if (nargs != 2) 430 return -1; 431 432 memset(&tz, 0, sizeof(tz)); 433 tz.tz_minuteswest = atoi(args[1]); 434 if (settimeofday(NULL, &tz)) 435 return -1; 436 return 0; 437} 438 439int do_write(int nargs, char **args) 440{ 441 return write_file(args[1], args[2]); 442} 443 444int do_copy(int nargs, char **args) 445{ 446 char *buffer = NULL; 447 int rc = 0; 448 int fd1 = -1, fd2 = -1; 449 struct stat info; 450 int brtw, brtr; 451 char *p; 452 453 if (nargs != 3) 454 return -1; 455 456 if (stat(args[1], &info) < 0) 457 return -1; 458 459 if ((fd1 = open(args[1], O_RDONLY)) < 0) 460 goto out_err; 461 462 if ((fd2 = open(args[2], O_WRONLY|O_CREAT, 0660)) < 0) 463 goto out_err; 464 465 if (!(buffer = malloc(info.st_size))) 466 goto out_err; 467 468 p = buffer; 469 brtr = info.st_size; 470 while(brtr) { 471 rc = read(fd1, p, brtr); 472 if (rc < 0) 473 goto out_err; 474 if (rc == 0) 475 break; 476 p += rc; 477 brtr -= rc; 478 } 479 480 p = buffer; 481 brtw = info.st_size; 482 while(brtw) { 483 rc = write(fd2, p, brtw); 484 if (rc < 0) 485 goto out_err; 486 if (rc == 0) 487 break; 488 p += rc; 489 brtw -= rc; 490 } 491 492 rc = 0; 493 goto out; 494out_err: 495 rc = -1; 496out: 497 if (buffer) 498 free(buffer); 499 if (fd1 >= 0) 500 close(fd1); 501 if (fd2 >= 0) 502 close(fd2); 503 return rc; 504} 505 506int do_chown(int nargs, char **args) { 507 /* GID is optional. */ 508 if (nargs == 3) { 509 if (chown(args[2], decode_uid(args[1]), -1) < 0) 510 return -errno; 511 } else if (nargs == 4) { 512 if (chown(args[3], decode_uid(args[1]), decode_uid(args[2]))) 513 return -errno; 514 } else { 515 return -1; 516 } 517 return 0; 518} 519 520static mode_t get_mode(const char *s) { 521 mode_t mode = 0; 522 while (*s) { 523 if (*s >= '0' && *s <= '7') { 524 mode = (mode<<3) | (*s-'0'); 525 } else { 526 return -1; 527 } 528 s++; 529 } 530 return mode; 531} 532 533int do_chmod(int nargs, char **args) { 534 mode_t mode = get_mode(args[1]); 535 if (chmod(args[2], mode) < 0) { 536 return -errno; 537 } 538 return 0; 539} 540 541int do_loglevel(int nargs, char **args) { 542 if (nargs == 2) { 543 log_set_level(atoi(args[1])); 544 return 0; 545 } 546 return -1; 547} 548 549int do_device(int nargs, char **args) { 550 int len; 551 char tmp[64]; 552 char *source = args[1]; 553 int prefix = 0; 554 555 if (nargs != 5) 556 return -1; 557 /* Check for wildcard '*' at the end which indicates a prefix. */ 558 len = strlen(args[1]) - 1; 559 if (args[1][len] == '*') { 560 args[1][len] = '\0'; 561 prefix = 1; 562 } 563 /* If path starts with mtd@ lookup the mount number. */ 564 if (!strncmp(source, "mtd@", 4)) { 565 int n = mtd_name_to_number(source + 4); 566 if (n >= 0) { 567 snprintf(tmp, sizeof(tmp), "/dev/mtd/mtd%d", n); 568 source = tmp; 569 } 570 } 571 add_devperms_partners(source, get_mode(args[2]), decode_uid(args[3]), 572 decode_uid(args[4]), prefix); 573 return 0; 574} 575