unix/minit

shutdown.c in master
Repositories | Summary | Log | Files | README | COPYING

shutdown.c (6769B) download


  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>
 36static inline int __write1(const char*s) { return write(1,s,str_len(s)); }
 37static 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
 48extern void opendevconsole();
 49
 50extern char **environ;
 51extern int openreadclose(char *fn, char **buf, size_t *len);
 52extern char **split(char *buf,int c,size_t* len,size_t plus,size_t ofs);
 53extern char *optarg;
 54
 55void wall(char *buf) {
 56  __write2(buf);
 57}
 58
 59int 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
 83static int infd, outfd;
 84static char buf[1500];
 85
 86int 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
130int 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
149void 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
160int 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}