OSDN Git Service

Init: Setting system time back to utc
[android-x86/system-core.git] / init / builtins.c
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 <sys/wait.h>
33 #include <linux/loop.h>
34 #include <cutils/partition_utils.h>
35 #include <sys/system_properties.h>
36 #include <fs_mgr.h>
37 #include <fnmatch.h>
38 #include <dirent.h>
39 #include <cutils/probe_module.h>
40 #include <time.h>
41
42 #ifdef HAVE_SELINUX
43 #include <selinux/selinux.h>
44 #include <selinux/label.h>
45 #endif
46
47 #include "init.h"
48 #include "keywords.h"
49 #include "property_service.h"
50 #include "devices.h"
51 #include "init_parser.h"
52 #include "util.h"
53 #include "log.h"
54
55 #include <private/android_filesystem_config.h>
56
57 #define TIMEZONE "/data/property/persist.sys.timezone"
58
59 enum builtin_cmds {
60     DO_CHOWN,
61     DO_CHMOD,
62 };
63
64 #define MAX_RECUR_DEPTH 15
65
66 void add_environment(const char *name, const char *value);
67
68 extern int init_module(void *, unsigned long, const char *);
69
70 static int write_file(const char *path, const char *value)
71 {
72     int fd, ret, len;
73
74     fd = open(path, O_WRONLY|O_CREAT, 0622);
75
76     if (fd < 0)
77         return -errno;
78
79     len = strlen(value);
80
81     do {
82         ret = write(fd, value, len);
83     } while (ret < 0 && errno == EINTR);
84
85     close(fd);
86     if (ret < 0) {
87         return -errno;
88     } else {
89         return 0;
90     }
91 }
92
93 static int _open(const char *path)
94 {
95     int fd;
96
97     fd = open(path, O_RDONLY | O_NOFOLLOW);
98     if (fd < 0)
99         fd = open(path, O_WRONLY | O_NOFOLLOW);
100
101     return fd;
102 }
103
104 /* chown or chmod for one item (file, directory, etc.) */
105 static int __chown_chmod_one(enum builtin_cmds action, const char *path,
106                              unsigned int uid, unsigned int gid, mode_t mode)
107 {
108     int fd;
109     int ret = -1;
110
111     fd = _open(path);
112     if (fd < 0) {
113         return -1;
114     }
115
116     if (action == DO_CHOWN) {
117         ret = fchown(fd, uid, gid);
118     } else if (action == DO_CHMOD) {
119         ret = fchmod(fd, mode);
120     }
121
122     if (ret < 0) {
123         int errno_copy = errno;
124         close(fd);
125         errno = errno_copy;
126         return -1;
127     }
128
129     close(fd);
130
131     return ret;
132 }
133
134 /* do chown or chmod recursively with pattern matching */
135 static int __chown_chmod_recur(enum builtin_cmds action, const char *matching_path,
136                                unsigned int uid, unsigned int gid, mode_t mode,
137                                const char *cur_path, int depth)
138 {
139     DIR* dirp;
140     struct dirent *de;
141     char* child_path = NULL;
142     int len;
143     int ret = 0;
144
145     if (!matching_path) {
146         return -1;
147     }
148     if (!cur_path) {
149         return -1;
150     }
151     if (depth > MAX_RECUR_DEPTH) {
152         /* prevent this from doing infinitely recurison */
153         return 0;
154     }
155
156     dirp = opendir(cur_path);
157     if (dirp) {
158         while ((de = readdir(dirp))) {
159             if ((de->d_type == DT_DIR) &&
160                 (de->d_name[0] == '.') &&
161                 ((de->d_name[1] == '\0') ||
162                  ((de->d_name[1] == '.') && (de->d_name[2] == '\0')))) {
163                 /* ignore directories "." and ".." */
164                 continue;
165             }
166
167             /* prepare path */
168             len = asprintf(&child_path, "%s/%s", cur_path, de->d_name);
169
170             if (len != -1) {
171                 if (de->d_type == DT_DIR) {
172                     /* recurse into lowering level directory */
173                     ret += __chown_chmod_recur(action, matching_path,
174                                                uid, gid, mode, child_path,
175                                                (depth + 1));
176                 } else {
177                     if (fnmatch(matching_path, child_path, FNM_PATHNAME) == 0) {
178                         /* FNM_PATHNAME: need to have the same number of '/' */
179                         ret += __chown_chmod_one(action, child_path,
180                                                  uid, gid, mode);
181                     }
182                 }
183
184                 free(child_path);
185                 child_path = NULL;
186             } else {
187                 /* failed to allocate space for child_path, */
188                 /* count as one failure.                    */
189                 ret += -1;
190             }
191         }
192
193         closedir(dirp);
194     }
195
196     return ret;
197 }
198
199 static int __chown_chmod(unsigned int action, const char *path,
200                          unsigned int uid, unsigned int gid, mode_t mode)
201 {
202     char* leading_path = NULL;
203     char* tmp = NULL;
204     int do_wildcard = 0;
205     int len;
206     int ret;
207
208     if (!path) {
209         return -1;
210     }
211
212     /* need wildcard matching? */
213     tmp = strchr(path, '*');
214     if (tmp) {
215         /* this block shorten the path for matching purpose */
216
217         leading_path = strdup(path);
218         if (!leading_path) {
219             return -1;
220         }
221
222         do_wildcard = 1;
223
224         /* get path before '*' */
225         tmp = strchr(leading_path, '*');
226         if (tmp) {
227             *tmp = '\0';
228         }
229
230         /* remove up to and including the last '/' */
231         tmp = strrchr(leading_path, '/');
232         if (tmp) {
233             *tmp = '\0';
234             do_wildcard = 1;
235         }
236     }
237
238     if (do_wildcard) {
239         ret = __chown_chmod_recur(action, path,
240                                    uid, gid, mode, leading_path, 0);
241
242         if (leading_path) {
243             free(leading_path);
244         }
245
246         return ret;
247     } else {
248         return __chown_chmod_one(action, path, uid, gid, mode);
249     }
250 }
251
252 static int _chown(const char *path, unsigned int uid, unsigned int gid)
253 {
254     return __chown_chmod(DO_CHOWN, path, uid, gid, 0);
255 }
256
257 static int _chmod(const char *path, mode_t mode)
258 {
259     return __chown_chmod(DO_CHMOD, path, -1, -1, mode);
260 }
261
262 static int insmod(const char *filename, char *options)
263 {
264     void *module;
265     unsigned size;
266     int ret;
267
268     module = read_file(filename, &size);
269     if (!module)
270         return -1;
271
272     ret = init_module(module, size, options);
273
274     free(module);
275
276     return ret;
277 }
278
279 static int setkey(struct kbentry *kbe)
280 {
281     int fd, ret;
282
283     fd = open("/dev/tty0", O_RDWR | O_SYNC);
284     if (fd < 0)
285         return -1;
286
287     ret = ioctl(fd, KDSKBENT, kbe);
288
289     close(fd);
290     return ret;
291 }
292
293 static int __ifupdown(const char *interface, int up)
294 {
295     struct ifreq ifr;
296     int s, ret;
297
298     strlcpy(ifr.ifr_name, interface, IFNAMSIZ);
299
300     s = socket(AF_INET, SOCK_DGRAM, 0);
301     if (s < 0)
302         return -1;
303
304     ret = ioctl(s, SIOCGIFFLAGS, &ifr);
305     if (ret < 0) {
306         goto done;
307     }
308
309     if (up)
310         ifr.ifr_flags |= IFF_UP;
311     else
312         ifr.ifr_flags &= ~IFF_UP;
313
314     ret = ioctl(s, SIOCSIFFLAGS, &ifr);
315     
316 done:
317     close(s);
318     return ret;
319 }
320
321 static void service_start_if_not_disabled(struct service *svc)
322 {
323     if (!(svc->flags & SVC_DISABLED)) {
324         service_start(svc, NULL);
325     }
326 }
327
328 int do_chdir(int nargs, char **args)
329 {
330     chdir(args[1]);
331     return 0;
332 }
333
334 int do_chroot(int nargs, char **args)
335 {
336     chroot(args[1]);
337     return 0;
338 }
339
340 int do_class_start(int nargs, char **args)
341 {
342         /* Starting a class does not start services
343          * which are explicitly disabled.  They must
344          * be started individually.
345          */
346     service_for_each_class(args[1], service_start_if_not_disabled);
347     return 0;
348 }
349
350 int do_class_stop(int nargs, char **args)
351 {
352     service_for_each_class(args[1], service_stop);
353     return 0;
354 }
355
356 int do_class_reset(int nargs, char **args)
357 {
358     service_for_each_class(args[1], service_reset);
359     return 0;
360 }
361
362 int do_domainname(int nargs, char **args)
363 {
364     return write_file("/proc/sys/kernel/domainname", args[1]);
365 }
366
367 int do_exec(int nargs, char **args)
368 {
369     return -1;
370 }
371
372 int do_export(int nargs, char **args)
373 {
374     add_environment(args[1], args[2]);
375     return 0;
376 }
377
378 int do_hostname(int nargs, char **args)
379 {
380     return write_file("/proc/sys/kernel/hostname", args[1]);
381 }
382
383 int do_ifup(int nargs, char **args)
384 {
385     return __ifupdown(args[1], 1);
386 }
387
388
389 static int do_insmod_inner(int nargs, char **args, int opt_len)
390 {
391     char options[opt_len + 1];
392     int i;
393
394     options[0] = '\0';
395     if (nargs > 2) {
396         strcpy(options, args[2]);
397         for (i = 3; i < nargs; ++i) {
398             strcat(options, " ");
399             strcat(options, args[i]);
400         }
401     }
402
403     return insmod(args[1], options);
404 }
405
406 int do_insmod(int nargs, char **args)
407 {
408     int i;
409     int size = 0;
410
411     if (nargs > 2) {
412         for (i = 2; i < nargs; ++i)
413             size += strlen(args[i]) + 1;
414     }
415
416     return do_insmod_inner(nargs, args, size);
417 }
418
419 static int do_probemod_inner(int nargs, char **args, int opt_len)
420 {
421     char options[opt_len + 1];
422     int i;
423     int ret;
424
425     options[0] = '\0';
426     if (nargs > 2) {
427         strcpy(options, args[2]);
428         for (i = 3; i < nargs; ++i) {
429             strcat(options, " ");
430             strcat(options, args[i]);
431         }
432     }
433
434     ret = insmod_by_dep(args[1], options, NULL, 1, NULL);
435     if (ret)
436         ERROR("Couldn't probe module '%s'\n", args[1]);
437     return ret;
438 }
439
440 int do_probemod(int nargs, char **args)
441 {
442     int i;
443     int size = 0;
444
445     if (nargs > 2) {
446         for (i = 2; i < nargs; ++i)
447             size += strlen(args[i]) + 1;
448     }
449
450     return do_probemod_inner(nargs, args, size);
451 }
452
453 int do_mkdir(int nargs, char **args)
454 {
455     mode_t mode = 0755;
456     int ret;
457
458     /* mkdir <path> [mode] [owner] [group] */
459
460     if (nargs >= 3) {
461         mode = strtoul(args[2], 0, 8);
462     }
463
464     ret = make_dir(args[1], mode);
465     /* chmod in case the directory already exists */
466     if (ret == -1 && errno == EEXIST) {
467         ret = _chmod(args[1], mode);
468     }
469     if (ret == -1) {
470         return -errno;
471     }
472
473     if (nargs >= 4) {
474         uid_t uid = decode_uid(args[3]);
475         gid_t gid = -1;
476
477         if (nargs == 5) {
478             gid = decode_uid(args[4]);
479         }
480
481         if (_chown(args[1], uid, gid) < 0) {
482             return -errno;
483         }
484
485         /* chown may have cleared S_ISUID and S_ISGID, chmod again */
486         if (mode & (S_ISUID | S_ISGID)) {
487             ret = _chmod(args[1], mode);
488             if (ret == -1) {
489                 return -errno;
490             }
491         }
492     }
493
494     return 0;
495 }
496
497 static struct {
498     const char *name;
499     unsigned flag;
500 } mount_flags[] = {
501     { "noatime",    MS_NOATIME },
502     { "noexec",     MS_NOEXEC },
503     { "nosuid",     MS_NOSUID },
504     { "nodev",      MS_NODEV },
505     { "nodiratime", MS_NODIRATIME },
506     { "ro",         MS_RDONLY },
507     { "rw",         0 },
508     { "remount",    MS_REMOUNT },
509     { "bind",       MS_BIND },
510     { "rec",        MS_REC },
511     { "unbindable", MS_UNBINDABLE },
512     { "private",    MS_PRIVATE },
513     { "slave",      MS_SLAVE },
514     { "shared",     MS_SHARED },
515     { "defaults",   0 },
516     { 0,            0 },
517 };
518
519 #define DATA_MNT_POINT "/data"
520
521 /* mount <type> <device> <path> <flags ...> <options> */
522 int do_mount(int nargs, char **args)
523 {
524     char tmp[64];
525     char *source, *target, *system;
526     char *options = NULL;
527     unsigned flags = 0;
528     int n, i;
529     int wait = 0;
530
531     for (n = 4; n < nargs; n++) {
532         for (i = 0; mount_flags[i].name; i++) {
533             if (!strcmp(args[n], mount_flags[i].name)) {
534                 flags |= mount_flags[i].flag;
535                 break;
536             }
537         }
538
539         if (!mount_flags[i].name) {
540             if (!strcmp(args[n], "wait"))
541                 wait = 1;
542             /* if our last argument isn't a flag, wolf it up as an option string */
543             else if (n + 1 == nargs)
544                 options = args[n];
545         }
546     }
547
548     system = args[1];
549     source = args[2];
550     target = args[3];
551
552     if (!strncmp(source, "mtd@", 4)) {
553         n = mtd_name_to_number(source + 4);
554         if (n < 0) {
555             return -1;
556         }
557
558         sprintf(tmp, "/dev/block/mtdblock%d", n);
559
560         if (wait)
561             wait_for_file(tmp, COMMAND_RETRY_TIMEOUT);
562         if (mount(tmp, target, system, flags, options) < 0) {
563             return -1;
564         }
565
566         goto exit_success;
567     } else if (!strncmp(source, "loop@", 5)) {
568         int mode, loop, fd;
569         struct loop_info info;
570
571         mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
572         fd = open(source + 5, mode);
573         if (fd < 0) {
574             return -1;
575         }
576
577         for (n = 0; ; n++) {
578             sprintf(tmp, "/dev/block/loop%d", n);
579             loop = open(tmp, mode);
580             if (loop < 0) {
581                 return -1;
582             }
583
584             /* if it is a blank loop device */
585             if (ioctl(loop, LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
586                 /* if it becomes our loop device */
587                 if (ioctl(loop, LOOP_SET_FD, fd) >= 0) {
588                     close(fd);
589
590                     if (mount(tmp, target, system, flags, options) < 0) {
591                         ioctl(loop, LOOP_CLR_FD, 0);
592                         close(loop);
593                         return -1;
594                     }
595
596                     close(loop);
597                     goto exit_success;
598                 }
599             }
600
601             close(loop);
602         }
603
604         close(fd);
605         ERROR("out of loopback devices");
606         return -1;
607     } else {
608         if (wait)
609             wait_for_file(source, COMMAND_RETRY_TIMEOUT);
610         if (mount(source, target, system, flags, options) < 0) {
611             return -1;
612         }
613
614     }
615
616 exit_success:
617     return 0;
618
619 }
620
621 int do_mount_all(int nargs, char **args)
622 {
623     pid_t pid;
624     int ret = -1;
625     int child_ret = -1;
626     int status;
627     const char *prop;
628
629     if (nargs != 2) {
630         return -1;
631     }
632
633     /*
634      * Call fs_mgr_mount_all() to mount all filesystems.  We fork(2) and
635      * do the call in the child to provide protection to the main init
636      * process if anything goes wrong (crash or memory leak), and wait for
637      * the child to finish in the parent.
638      */
639     pid = fork();
640     if (pid > 0) {
641         /* Parent.  Wait for the child to return */
642         waitpid(pid, &status, 0);
643         if (WIFEXITED(status)) {
644             ret = WEXITSTATUS(status);
645         } else {
646             ret = -1;
647         }
648     } else if (pid == 0) {
649         /* child, call fs_mgr_mount_all() */
650         klog_set_level(6);  /* So we can see what fs_mgr_mount_all() does */
651         child_ret = fs_mgr_mount_all(args[1]);
652         if (child_ret == -1) {
653             ERROR("fs_mgr_mount_all returned an error\n");
654         }
655         exit(child_ret);
656     } else {
657         /* fork failed, return an error */
658         return -1;
659     }
660
661     /* ret is 1 if the device is encrypted, 0 if not, and -1 on error */
662     if (ret == 1) {
663         property_set("ro.crypto.state", "encrypted");
664         property_set("vold.decrypt", "1");
665     } else if (ret == 0) {
666         property_set("ro.crypto.state", "unencrypted");
667         /* If fs_mgr determined this is an unencrypted device, then trigger
668          * that action.
669          */
670         action_for_each_trigger("nonencrypted", action_add_queue_tail);
671     }
672
673     return ret;
674 }
675
676 int do_setcon(int nargs, char **args) {
677 #ifdef HAVE_SELINUX
678     if (is_selinux_enabled() <= 0)
679         return 0;
680     if (setcon(args[1]) < 0) {
681         return -errno;
682     }
683 #endif
684     return 0;
685 }
686
687 int do_setenforce(int nargs, char **args) {
688 #ifdef HAVE_SELINUX
689     if (is_selinux_enabled() <= 0)
690         return 0;
691     if (security_setenforce(atoi(args[1])) < 0) {
692         return -errno;
693     }
694 #endif
695     return 0;
696 }
697
698 int do_setkey(int nargs, char **args)
699 {
700     struct kbentry kbe;
701     kbe.kb_table = strtoul(args[1], 0, 0);
702     kbe.kb_index = strtoul(args[2], 0, 0);
703     kbe.kb_value = strtoul(args[3], 0, 0);
704     return setkey(&kbe);
705 }
706
707 int do_builtin_coldboot(int nargs, char **args)
708 {
709     if (nargs != 2 || !args[1] || *args[1] == '\0')
710         return -1;
711
712     coldboot(args[1]);
713
714     return 0;
715 }
716
717 int do_setprop(int nargs, char **args)
718 {
719     const char *name = args[1];
720     const char *value = args[2];
721
722     property_set(name, value);
723     return 0;
724 }
725
726 int do_setrlimit(int nargs, char **args)
727 {
728     struct rlimit limit;
729     int resource;
730     resource = atoi(args[1]);
731     limit.rlim_cur = atoi(args[2]);
732     limit.rlim_max = atoi(args[3]);
733     return setrlimit(resource, &limit);
734 }
735
736 int do_start(int nargs, char **args)
737 {
738     struct service *svc;
739     svc = service_find_by_name(args[1]);
740     if (svc) {
741         service_start(svc, NULL);
742     }
743     return 0;
744 }
745
746 int do_stop(int nargs, char **args)
747 {
748     struct service *svc;
749     svc = service_find_by_name(args[1]);
750     if (svc) {
751         service_stop(svc);
752     }
753     return 0;
754 }
755
756 int do_restart(int nargs, char **args)
757 {
758     struct service *svc;
759     svc = service_find_by_name(args[1]);
760     if (svc) {
761         service_stop(svc);
762         service_start(svc, NULL);
763     }
764     return 0;
765 }
766
767 int do_trigger(int nargs, char **args)
768 {
769     action_for_each_trigger(args[1], action_add_queue_tail);
770     return 0;
771 }
772
773 int do_symlink(int nargs, char **args)
774 {
775     return symlink(args[1], args[2]);
776 }
777
778 int do_rm(int nargs, char **args)
779 {
780     return unlink(args[1]);
781 }
782
783 int do_rmdir(int nargs, char **args)
784 {
785     return rmdir(args[1]);
786 }
787
788 int do_sysclktz(int nargs, char **args)
789 {
790     struct timezone tz;
791     struct timeval tv;
792     struct tm tm;
793     FILE *fp;
794     char *line = NULL;
795     size_t len = 0;
796     char const *hwtime_mode;
797     time_t t;
798
799     if (nargs != 2)
800         return -1;
801
802     memset(&tz, 0, sizeof(tz));
803     memset(&tv, 0, sizeof(tv));
804     memset(&tm, 0, sizeof(tm));
805
806     if (!strcmp(args[1], "0")) {
807         tz.tz_minuteswest = atoi(args[1]);
808         if (settimeofday(NULL, &tz))
809             return -1;
810         return 0;
811     }
812
813     if (gettimeofday(&tv, NULL))
814         return -1;
815     hwtime_mode = property_get("ro.rtc_local_time");
816     if (hwtime_mode && !strcmp(hwtime_mode, "1")
817                     && !strcmp(args[1], "1")) {
818
819         /* Notify kernel that hwtime use local time */
820         write_file("/sys/class/misc/alarm/rtc_local_time",
821                     hwtime_mode);
822         /*
823          * If ro.hwtime.mode is local, set system time
824          * and saved system zone in case of network not
825          * available and auto syncing time not available.
826          */
827         if (access(TIMEZONE, 0) == 0) {
828             fp = fopen(TIMEZONE, "r+");
829             if (fp == NULL)
830                 return -1;
831
832             if (getline(&line, &len, fp) == -1)
833                 tz.tz_minuteswest = 0;
834             else {
835                 /* Hack to get timezone. */
836                 for (len = 0; *(line+len) != '\n' && *(line+len) != 0; len++);
837                 *(line+len) = '\0';
838                 property_set("persist.sys.timezone", line);
839                 t = tv.tv_sec;
840                 localtime_r(&t, &tm);
841                 tz.tz_minuteswest = -(tm.tm_gmtoff / 60);
842             }
843             free(line);
844             fclose(fp);
845         }
846         else
847             tz.tz_minuteswest = 0;
848
849         /*
850          * At this moment, system time should be local
851          * time too, set it back to utc which linux required.
852          */
853         tv.tv_sec += tz.tz_minuteswest * 60;
854         if (settimeofday(&tv, &tz))
855             return -1;
856     } else {
857         return -1;
858     }
859     return 0;
860 }
861
862 int do_write(int nargs, char **args)
863 {
864     const char *path = args[1];
865     const char *value = args[2];
866
867     return write_file(path, value);
868 }
869
870 int do_copy(int nargs, char **args)
871 {
872     char *buffer = NULL;
873     int rc = 0;
874     int fd1 = -1, fd2 = -1;
875     struct stat info;
876     int brtw, brtr;
877     char *p;
878
879     if (nargs != 3)
880         return -1;
881
882     if (stat(args[1], &info) < 0) 
883         return -1;
884
885     if ((fd1 = open(args[1], O_RDONLY)) < 0) 
886         goto out_err;
887
888     if ((fd2 = open(args[2], O_WRONLY|O_CREAT|O_TRUNC, 0660)) < 0)
889         goto out_err;
890
891     if (!(buffer = malloc(info.st_size)))
892         goto out_err;
893
894     p = buffer;
895     brtr = info.st_size;
896     while(brtr) {
897         rc = read(fd1, p, brtr);
898         if (rc < 0)
899             goto out_err;
900         if (rc == 0)
901             break;
902         p += rc;
903         brtr -= rc;
904     }
905
906     p = buffer;
907     brtw = info.st_size;
908     while(brtw) {
909         rc = write(fd2, p, brtw);
910         if (rc < 0)
911             goto out_err;
912         if (rc == 0)
913             break;
914         p += rc;
915         brtw -= rc;
916     }
917
918     rc = 0;
919     goto out;
920 out_err:
921     rc = -1;
922 out:
923     if (buffer)
924         free(buffer);
925     if (fd1 >= 0)
926         close(fd1);
927     if (fd2 >= 0)
928         close(fd2);
929     return rc;
930 }
931
932 int do_chown(int nargs, char **args) {
933     /* GID is optional. */
934     if (nargs == 3) {
935         if (_chown(args[2], decode_uid(args[1]), -1) < 0)
936             return -errno;
937     } else if (nargs == 4) {
938         if (_chown(args[3], decode_uid(args[1]), decode_uid(args[2])) < 0)
939             return -errno;
940     } else {
941         return -1;
942     }
943     return 0;
944 }
945
946 static mode_t get_mode(const char *s) {
947     mode_t mode = 0;
948     while (*s) {
949         if (*s >= '0' && *s <= '7') {
950             mode = (mode<<3) | (*s-'0');
951         } else {
952             return -1;
953         }
954         s++;
955     }
956     return mode;
957 }
958
959 int do_chmod(int nargs, char **args) {
960     mode_t mode = get_mode(args[1]);
961     if (_chmod(args[2], mode) < 0) {
962         return -errno;
963     }
964     return 0;
965 }
966
967 int do_restorecon(int nargs, char **args) {
968     int i;
969
970     for (i = 1; i < nargs; i++) {
971         if (restorecon(args[i]) < 0)
972             return -errno;
973     }
974     return 0;
975 }
976
977 int do_setsebool(int nargs, char **args) {
978 #ifdef HAVE_SELINUX
979     SELboolean *b = alloca(nargs * sizeof(SELboolean));
980     char *v;
981     int i;
982
983     if (is_selinux_enabled() <= 0)
984         return 0;
985
986     for (i = 1; i < nargs; i++) {
987         char *name = args[i];
988         v = strchr(name, '=');
989         if (!v) {
990             ERROR("setsebool: argument %s had no =\n", name);
991             return -EINVAL;
992         }
993         *v++ = 0;
994         b[i-1].name = name;
995         if (!strcmp(v, "1") || !strcasecmp(v, "true") || !strcasecmp(v, "on"))
996             b[i-1].value = 1;
997         else if (!strcmp(v, "0") || !strcasecmp(v, "false") || !strcasecmp(v, "off"))
998             b[i-1].value = 0;
999         else {
1000             ERROR("setsebool: invalid value %s\n", v);
1001             return -EINVAL;
1002         }
1003     }
1004
1005     if (security_set_boolean_list(nargs - 1, b, 0) < 0)
1006         return -errno;
1007 #endif
1008     return 0;
1009 }
1010
1011 int do_loglevel(int nargs, char **args) {
1012     if (nargs == 2) {
1013         klog_set_level(atoi(args[1]));
1014         return 0;
1015     }
1016     return -1;
1017 }
1018
1019 int do_load_persist_props(int nargs, char **args) {
1020     if (nargs == 1) {
1021         load_persist_props();
1022         return 0;
1023     }
1024     return -1;
1025 }
1026
1027 int do_wait(int nargs, char **args)
1028 {
1029     if (nargs == 2) {
1030         return wait_for_file(args[1], COMMAND_RETRY_TIMEOUT);
1031     } else if (nargs == 3) {
1032         return wait_for_file(args[1], atoi(args[2]));
1033     } else
1034         return -1;
1035 }
1036
1037 int do_readprops(int nargs, char **args)
1038 {
1039     if (nargs == 2) {
1040         return load_properties_from_file(args[1]);
1041     }
1042     return -1;
1043 }
1044