minit

A small yet feature-complete init (http://fefe.de/minit/)
Log | Files | Refs | README | LICENSE

shutdown.c (6769B)


      1 /*
      2  * Notes:
      3  * - uncomment `#define ALLOW_SUID' below if you want users other than
      4  *   root to be able to shut down the system. 
      5  * - after compiling, install under /sbin/shutdown with chgrp adm and 
      6  *   chmod 4750 for SUID root, or 0700 for root only
      7  * - uncomment `#define USE_MINIT' below if you want to use shutdown
      8  *   with minit. If defined, shutdown will try to bring down the services 
      9  *   halt (for -h or -o) or reboot (-r) before killing all other processes.
     10  *   Please make sure that you have a depends in reboot and halt that 
     11  *   will bring down all respawning services. A respawning service during
     12  *   shutdown might cause you to wait for a fsck during the next boot
     13  * - If you do not use minit shutdown will bring your system down similar
     14  *   to SysV-Inits shutdown with -n
     15  *
     16  * TODO: 
     17  * - add a function for wall-messages
     18  * - cleanup
     19  */
     20 
     21 #include <sys/types.h>
     22 #include <sys/reboot.h>
     23 #include <signal.h>
     24 #include <fcntl.h>
     25 #include <stdarg.h>
     26 #include <stdio.h>
     27 #include <stdlib.h>
     28 #include <sys/wait.h>
     29 
     30 #include <libowfat/str.h>
     31 
     32 #ifdef __dietlibc__
     33 #include <write12.h>
     34 #else
     35 #include <unistd.h>
     36 static inline int __write1(const char*s) { return write(1,s,str_len(s)); }
     37 static inline int __write2(const char*s) { return write(2,s,str_len(s)); }
     38 #endif
     39 
     40 #define ALLOW_SUID
     41 #define USE_MINIT
     42 
     43 #ifdef USE_MINIT
     44 #define NOVARS
     45 #include "minit.h"
     46 #endif
     47 
     48 extern void opendevconsole();
     49 
     50 extern char **environ;
     51 extern int openreadclose(char *fn, char **buf, size_t *len);
     52 extern char **split(char *buf,int c,size_t* len,size_t plus,size_t ofs);
     53 extern char *optarg;
     54 
     55 void wall(char *buf) {
     56   __write2(buf);
     57 }
     58 
     59 int exec_cmd(char *cmd, ...) {
     60   char *argv[10];
     61   va_list arguments;
     62   pid_t pid;
     63   int i;
     64 
     65   va_start(arguments, cmd);
     66   for (i=0;i<9 && (argv[i] = va_arg(arguments, char *)) != NULL; i++);
     67   argv[i] = NULL;
     68   va_end(arguments);
     69   pid = fork();
     70   if (pid < 0) return -1;
     71   if (pid > 0) {
     72     wait(NULL);
     73   } else {
     74     execve(cmd, argv, environ);
     75     //perror("execvp failed");
     76     exit(0);
     77   }
     78   return 0;
     79 }
     80 
     81 #ifdef USE_MINIT
     82 
     83 static int infd, outfd;
     84 static char buf[1500];
     85 
     86 int minit_serviceDown(char *service) {
     87   char *s=0;
     88   unsigned long len;
     89   pid_t pid=0;
     90   
     91   if (!service || !*service) return 0;
     92   if (chdir(MINITROOT) || chdir(service)) return -1;
     93   
     94   if (!openreadclose("depends", &s, &len)) {
     95     char **deps;
     96     size_t depc, i;
     97     deps=split(s, '\n', &depc, 0, 0);
     98     for (i=0; i<depc; i++) {
     99       if (deps[i][0] == '#') continue;
    100       minit_serviceDown(deps[i]);
    101     }
    102   }
    103   
    104   // get the pid
    105   buf[0]='p';
    106   strncpy(buf+1, service, 1400);
    107   write(infd, buf, str_len(buf));
    108   len=read(outfd, buf, 1500);
    109   if (len != 0) {
    110     buf[len]=0;
    111     pid = atoi(buf);
    112   }
    113 
    114   if (strcmp("reboot",service) && strcmp("halt",service) && pid > 1) {
    115     int i;
    116     __write2("\t--> "); __write2(service);
    117     buf[0]='r'; // we want to disable respawning first
    118     strncpy(buf+1, service, 1400);
    119     buf[1400]=0;
    120     write(infd, buf, str_len(buf));
    121     if (read(outfd, buf, 1500) < 1)
    122       __write2("\t(status read failed)");
    123     i=kill(pid, SIGTERM);
    124     if (i == 0) __write2("\t\tdone\n");
    125     else __write2("\t\tfailed\n");
    126   }
    127   return 0;
    128 }
    129 
    130 int minit_shutdown(int level) {
    131   int retval;
    132 
    133   __write2("Shutting down minit services: \n");
    134   infd=open(MINITROOT "/in", O_WRONLY);
    135   outfd=open(MINITROOT "/out", O_RDONLY);
    136   if (infd>=0) {
    137     while (lockf(infd, F_TLOCK, 1)) {
    138       __write2("could not acquire lock!\n");
    139       sleep(1);
    140     }  
    141   }
    142 
    143   retval=minit_serviceDown(level?"halt":"reboot");
    144   close(infd); close(outfd);
    145   return retval;
    146 }
    147 #endif
    148 
    149 void printUsage() {
    150   __write2("usage: shutdown -[rhosmn] -[t secs]\n"
    151                 "\t -r:        reboot after shutdown\n"
    152                 "\t -h:        halt after shutdown\n"
    153 		"\t -o:	       power-off after shutdown\n"
    154 		"\t -s:	       single-user console after shutdown\n"
    155 		"\t -m:	       only shutdown the minit-part\n"
    156                 "\t -n:        do not shutdown services using minit\n"
    157       		"\t -t secs:   delay between SIGTERM and SIGKILL\n");
    158 }
    159 
    160 int main(int argc, char *const argv[]) {
    161   int c;
    162   int cfg_downlevel=2;
    163   /* 0: reboot
    164    * 1: halt
    165    * 2: power off
    166    */
    167   unsigned int cfg_delay = 3;
    168   int cfg_minitonly = 0;
    169   int cfg_sulogin = 0;
    170 
    171   #ifdef ALLOW_SUID
    172   if (setuid(geteuid()) == -1) {
    173     __write2("setuid(geteuid()) failed.\n");
    174     return 111;
    175   }
    176   #endif
    177   if (getuid() != 0) {
    178 	  __write2("you are not root, go away!\n");
    179 	  return 1;
    180   }
    181 
    182   if (argc<2) {
    183     printUsage();
    184     return 0;
    185   }
    186 
    187   /* parse commandline options */
    188   while((c = getopt(argc, argv, "rhosmnt:")) != EOF) {
    189     switch(c) {
    190       case 'r': /* do we have to reboot... */
    191 	      cfg_downlevel = 0;
    192 	      break;
    193       case 'h': /* ...halt.. */
    194 	      cfg_downlevel = 1;
    195 	      break;
    196       case 's': /* rescue system */
    197 	      cfg_sulogin = 1;
    198 	      break;
    199       case 'm': /* exit after minit down */
    200 	      cfg_minitonly = 1;
    201 	      break;
    202       case 'o': /* ..or power off? */
    203 	      cfg_downlevel = 2;
    204 	      break;
    205       case 't': /* delay between SIGTERM and SIGKILL */
    206 	      cfg_delay = atoi(optarg);
    207 	      break;
    208       default:
    209 	      printUsage();
    210 	      return 1;
    211     }
    212   }
    213   
    214   opendevconsole();
    215   
    216   switch (cfg_downlevel) {
    217 	  case 0:
    218 		  wall("system is going down for reboot NOW\n");
    219 		  break;
    220 	  case 1:
    221 		  wall("system is going down for system halt NOW\n");
    222 		  break;
    223 	  case 2:
    224 		  wall("system is going down for power-off NOW\n");
    225 		  break;
    226   }
    227 
    228   /* 
    229    * catch some signals; 
    230    * getting killed after killing the controlling terminal wouldn't be 
    231    * such a great thing...
    232    */
    233   signal(SIGQUIT, SIG_IGN);
    234   signal(SIGCHLD, SIG_IGN);
    235   signal(SIGHUP,  SIG_IGN);
    236   signal(SIGTSTP, SIG_IGN);
    237   signal(SIGTTIN, SIG_IGN);
    238   signal(SIGTTOU, SIG_IGN);
    239  
    240   // real shutdown? then lets rock..
    241   #ifdef USE_MINIT
    242   minit_shutdown(cfg_downlevel);
    243   if (cfg_minitonly) return 0;
    244   #endif
    245   
    246   /* kill all processes still left */
    247   __write2("sending all processes the TERM signal...\n");
    248   kill(-1, SIGTERM);
    249   sleep(cfg_delay);
    250 
    251   __write2("sending all processes the KILL signal...\n");
    252   kill(-1, SIGKILL);
    253 
    254   if (cfg_sulogin) {
    255     char* suargs[]={"sulogin",0};
    256     execve("/sbin/sulogin", suargs, 0);
    257     __write2("execve() /sbin/sulogin failed\n");
    258     return 1;
    259   }
    260 
    261   /* sync buffers */
    262   sync();
    263 
    264   exec_cmd("/sbin/swapoff", "swapoff", "-a", (char *) 0);
    265   exec_cmd("/bin/umount", "umount", "-a", (char *) 0);
    266   exec_cmd("/bin/mount", "mount", "-o", "remount,ro", "/", (char *) 0);
    267 
    268   sync();
    269 
    270   /* and finally reboot, halt or power-off the system */ 
    271   if (cfg_downlevel == 0) {
    272     reboot(RB_AUTOBOOT);
    273   } else if (cfg_downlevel == 1) {
    274     reboot(RB_HALT_SYSTEM);
    275   } else {
    276     reboot(RB_POWER_OFF);
    277   }
    278   return 0;
    279 }