]> Pileus Git - mkinit/blob - src/initctld.c
Update mkinit
[mkinit] / src / initctld.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <errno.h>
4 #include <err.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7 #include <sys/stat.h>
8
9 #define INIT_MAGIC            0x03091969
10 #define INIT_CMD_START        0
11 #define INIT_CMD_RUNLVL       1
12 #define INIT_CMD_POWERFAIL    2
13 #define INIT_CMD_POWERFAILNOW 3
14 #define INIT_CMD_POWEROK      4
15 #define INIT_CMD_BSD          5
16 #define INIT_CMD_SETENV       6
17 #define INIT_CMD_UNSETENV     7
18 #define INIT_CMD_CHANGECONS   12345
19 #define INITRQ_HLEN           64
20
21 /**
22  * tellinit
23  *   0   shutdown
24  *   1   single
25  *   2   nonetwork
26  *   3   default
27  *   4   default
28  *   5   gui
29  *   6   reboot
30  *   abc ignored (inittab)
31  *   sS  single user
32  *   qQ  reload
33  *   uU  reload
34  * 
35  * shutdown
36  *   -r, reboot        runlevel 6 5, setenv INIT_HALT
37  *   -h, default halt  runlevel 0 5, setenv INIT_HALT
38  *   -P, poweroff      runlevel S 5, setenv INIT_HALT=POWERDOWN
39  *   -H, just halt     runlevel S 5, setenv INIT_HALT=HALT
40  *   -f, skip fsck     runlevel S 5, setenv INIT_HALT
41  *   -F, force fskc    runlevel S 5, setenv INIT_HALT
42  */
43
44 /**
45  * This is what BSD 4.4 uses when talking to init.
46  * Linux doesn't use this right now.
47  */
48 struct init_request_bsd {
49         char gen_id[8];         /* Beats me.. telnetd uses "fe" */
50         char tty_id[16];        /* Tty name minus /dev/tty      */
51         char host[INITRQ_HLEN]; /* Hostname                     */
52         char term_type[16];     /* Terminal type                */
53         int  signal;            /* Signal to send               */
54         int  pid;               /* Process to send to           */
55         char exec_name[128];    /* Program to execute           */
56         char reserved[128];     /* For future expansion.        */
57 };
58
59 /**
60  * Because of legacy interfaces, "runlevel" and "sleeptime" aren't in a
61  * seperate struct in the union.
62  *
63  * The weird sizes are because init expects the whole struct to be 384 bytes.
64  */
65 struct init_request {
66         int magic;     /* Magic number               */
67         int cmd;       /* What kind of request       */
68         int runlevel;  /* Runlevel to change to      */
69         int sleeptime; /* Time between TERM and KILL */
70         union {
71                 struct init_request_bsd bsd;
72                 char  data[368];
73         } i;
74 };
75
76 const char *telinit_map[0x100] = {
77         /* not sure if these are mapped correctly */
78         ['0'] "runlevel poweroff",
79         ['1'] "runlevel single",
80         ['2'] "runlevel bare",
81         ['3'] "runlevel system",
82         ['5'] "runlevel user",
83         ['6'] "runlevel reboot",
84         ['s'] "runlevel single",
85         ['S'] "runlevel single",
86         ['q'] "reload",
87         ['Q'] "reload",
88         ['u'] "reload",
89         ['U'] "reload",
90 };
91
92 int open_initctl(char *init_fifo)
93 {
94         /* First, try to create /dev/initctl if not present. */
95         struct stat st, st2;
96         if (stat(init_fifo, &st2) < 0 && errno == ENOENT)
97                 mkfifo(init_fifo, 0600);
98
99         /* Now finally try to open /dev/initctl */
100         int pipe_fd = open(init_fifo, O_RDWR);
101         if (pipe_fd < 0)
102                 err(errno, "error opening %s", init_fifo);
103
104         /* Make sure it's a fifo */
105         fstat(pipe_fd, &st);
106         if (!S_ISFIFO(st.st_mode)) {
107                 errno = EINVAL;
108                 err(errno, "%s is not a fifo", init_fifo);
109         }
110
111         return pipe_fd;
112 }
113
114 void process_request(struct init_request *request)
115 {
116         if (request->sleeptime)
117                 printf("eval SLEEP_TIME=%d\n", request->sleeptime);
118
119         switch (request->cmd) {
120         case INIT_CMD_RUNLVL:
121                 if (telinit_map[request->runlevel])
122                         printf("%s\n", telinit_map[request->runlevel]);
123                 break;
124         case INIT_CMD_POWERFAIL:
125                 printf("powerfail\n");
126                 break;
127         case INIT_CMD_POWERFAILNOW:
128                 printf("powerfailnow\n");
129                 break;
130         case INIT_CMD_POWEROK:
131                 printf("powerok\n");
132                 break;
133         case INIT_CMD_SETENV:
134                 printf("eval export %.*s\n",
135                                 (int)sizeof(request->i.data),
136                                 request->i.data);
137                 break;
138         case INIT_CMD_CHANGECONS:
139                 printf("changeconsole %.*s\n",
140                                 (int)sizeof(request->i.bsd.reserved),
141                                 request->i.bsd.reserved);
142                 break;
143         default:
144                 fprintf(stderr, "got unimplemented initrequest");
145                 break;
146         }
147         fflush(stdout);
148 }
149
150 int main(int argc, char **argv)
151 {
152         if (argc != 2) {
153                 printf("usage: %s <initctl>\n", argv[0]);
154                 return -EINVAL;
155         }
156         char *init_fifo = argv[1];
157         int pipe_fd = open_initctl(init_fifo);
158
159         /* Main loop, process data and reopen pipe when necessasairy */
160         while (1) {
161                 /* Read the data, return on EINTR. */
162                 struct init_request request;
163                 int n = read(pipe_fd, &request, sizeof(request));
164                 if (n == 0)
165                         pipe_fd = open_initctl(init_fifo);
166                 else if (n <= 0)
167                       warn("error reading request");
168                 else if (n != sizeof(request))
169                       warn("short read");
170                 else if (request.magic != INIT_MAGIC)
171                       warn("got bogus request");
172                 else
173                       process_request(&request);
174         }
175
176         return 0;
177 }