Add init script for ceod
[mspang/pyceo.git] / src / dmaster.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <signal.h>
5 #include <string.h>
6 #include <syslog.h>
7 #include <libgen.h>
8 #include <getopt.h>
9 #include <errno.h>
10 #include <netdb.h>
11 #include <alloca.h>
12 #include <fcntl.h>
13
14 #include "util.h"
15 #include "net.h"
16 #include "config.h"
17 #include "gss.h"
18 #include "daemon.h"
19 #include "ldap.h"
20 #include "kadm.h"
21 #include "krb5.h"
22 #include "ops.h"
23
24 static struct option opts[] = {
25     { "detach", 0, NULL, 'd' },
26     { "quiet", 0, NULL, 'q' },
27     { NULL, 0, NULL, '\0' },
28 };
29
30 char *prog = NULL;
31
32 int terminate = 0;
33 int fatal_signal;
34
35 static int detach = 0;
36
37 static void usage() {
38     fprintf(stderr, "Usage: %s [--detach]\n", prog);
39     exit(2);
40 }
41
42 static void signal_handler(int sig) {
43     if (sig == SIGTERM || sig == SIGINT) {
44         const char *s = (sig == SIGTERM) ? "terminated" : "interrupt";
45         notice("shutting down (%s)", s);
46         terminate = 1;
47         fatal_signal = sig;
48         signal(sig, SIG_DFL);
49     } else if (sig == SIGSEGV) {
50         error("segmentation fault");
51         signal(sig, SIG_DFL);
52         raise(sig);
53     } else if (sig != SIGCHLD) {
54         fatal("unhandled signal %d", sig);
55     }
56 }
57
58 static void setup_signals(void) {
59     struct sigaction sa;
60     memset(&sa, 0, sizeof(sa));
61     sigemptyset(&sa.sa_mask);
62     sa.sa_handler = signal_handler;
63
64     sigaction(SIGINT,  &sa, NULL);
65     sigaction(SIGTERM, &sa, NULL);
66     sigaction(SIGSEGV, &sa, NULL);
67
68     signal(SIGPIPE, SIG_IGN);
69     signal(SIGCHLD, SIG_IGN);
70 }
71
72 static void setup_pidfile(void) {
73     int fd;
74     size_t pidlen;
75     char pidbuf[1024];
76     const char *pidfile = "/var/run/ceod.pid";
77
78     fd = open(pidfile, O_CREAT|O_RDWR, 0644);
79     if (fd < 0)
80         fatalpe("open: %s", pidfile);
81     if (lockf(fd, F_TLOCK, 0))
82         fatalpe("lockf: %s", pidfile);
83     if (ftruncate(fd, 0))
84         fatalpe("ftruncate: %s", pidfile);
85     pidlen = snprintf(pidbuf, sizeof(pidbuf), "%d\n", getpid());
86     if (pidlen >= sizeof(pidbuf))
87         fatal("pid too long");
88     full_write(fd, pidbuf, pidlen);
89 }
90
91 static void setup_daemon(void) {
92     if (detach) {
93         if (chdir("/"))
94             fatalpe("chdir('/')");
95         pid_t pid = fork();
96         if (pid < 0)
97             fatalpe("fork");
98         if (pid)
99             exit(0);
100         if (setsid() < 0)
101             fatalpe("setsid");
102
103         setup_pidfile();
104
105         close(STDIN_FILENO);
106         close(STDOUT_FILENO);
107         close(STDERR_FILENO);
108     }
109 }
110
111 static void setup_auth(void) {
112     if (setenv("KRB5CCNAME", "MEMORY:ceod", 1))
113         fatalpe("setenv");
114     server_acquire_creds("ceod");
115 }
116
117 static void accept_one_client(int server) {
118     struct sockaddr_in addr;
119     socklen_t addrlen = sizeof(addr);
120     memset(&addr, 0, addrlen);
121
122     int client = accept(server, (sa *)&addr, &addrlen);
123     if (client < 0) {
124         if (errno == EINTR)
125             return;
126         fatalpe("accept");
127     }
128
129     pid_t pid = fork();
130     if (!pid) {
131         close(server);
132         slave_main(client, (sa *)&addr);
133         exit(0);
134     }
135
136     close(client);
137 }
138
139 static int master_main(void) {
140     int sock;
141     struct sockaddr_in addr;
142
143     memset(&addr, 0, sizeof(addr));
144     addr.sin_family = AF_INET;
145     addr.sin_port = htons(9987);
146     addr.sin_addr.s_addr = INADDR_ANY;
147
148     sock = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
149     if (sock < 0)
150         fatalpe("socket");
151
152     if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)))
153         fatalpe("bind");
154
155     if (listen(sock, 128))
156         fatalpe("listen");
157
158     setup_fqdn();
159     setup_signals();
160     setup_auth();
161     setup_ops();
162     setup_daemon();
163
164     notice("now accepting connections");
165
166     while (!terminate)
167         accept_one_client(sock);
168
169     free_gss();
170     free_fqdn();
171     free_ops();
172
173     return 0;
174 }
175
176 int main(int argc, char *argv[]) {
177     int opt;
178     int ret;
179
180     prog = xstrdup(basename(argv[0]));
181     init_log(prog, LOG_PID, LOG_DAEMON, 0);
182
183     while ((opt = getopt_long(argc, argv, "dq", opts, NULL)) != -1) {
184         switch (opt) {
185             case 'd':
186                 detach = 1;
187                 break;
188             case 'q':
189                 log_set_maxprio(LOG_WARNING);
190                 break;
191             case '?':
192                 usage();
193                 break;
194             default:
195                 fatal("error parsing arguments");
196         }
197     }
198
199     configure();
200
201     if (argc != optind)
202         usage();
203
204     ret = master_main();
205
206     free_config();
207     free(prog);
208
209     return ret;
210 }