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 }