Improve error handling when writing
[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     if (full_write(fd, pidbuf, pidlen))
89         fatalpe("write: %s", pidfile);
90 }
91
92 static void setup_daemon(void) {
93     if (detach) {
94         if (chdir("/"))
95             fatalpe("chdir('/')");
96         pid_t pid = fork();
97         if (pid < 0)
98             fatalpe("fork");
99         if (pid)
100             exit(0);
101         if (setsid() < 0)
102             fatalpe("setsid");
103
104         setup_pidfile();
105
106         freopen("/dev/null", "r", stdin);
107         freopen("/dev/null", "w", stdout);
108         freopen("/dev/null", "w", stderr);
109     }
110 }
111
112 static void setup_auth(void) {
113     if (setenv("KRB5CCNAME", "MEMORY:ceod", 1))
114         fatalpe("setenv");
115     server_acquire_creds("ceod");
116 }
117
118 static void accept_one_client(int server) {
119     struct sockaddr_in addr;
120     socklen_t addrlen = sizeof(addr);
121     memset(&addr, 0, addrlen);
122
123     int client = accept(server, (sa *)&addr, &addrlen);
124     if (client < 0) {
125         if (errno == EINTR)
126             return;
127         fatalpe("accept");
128     }
129
130     pid_t pid = fork();
131     if (!pid) {
132         close(server);
133         slave_main(client, (sa *)&addr);
134         exit(0);
135     }
136
137     close(client);
138 }
139
140 static int master_main(void) {
141     int sock;
142     struct sockaddr_in addr;
143
144     memset(&addr, 0, sizeof(addr));
145     addr.sin_family = AF_INET;
146     addr.sin_port = htons(9987);
147     addr.sin_addr.s_addr = INADDR_ANY;
148
149     sock = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
150     if (sock < 0)
151         fatalpe("socket");
152
153     if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)))
154         fatalpe("bind");
155
156     if (listen(sock, 128))
157         fatalpe("listen");
158
159     setup_fqdn();
160     setup_signals();
161     setup_auth();
162     setup_ops();
163     setup_daemon();
164
165     notice("now accepting connections");
166
167     while (!terminate)
168         accept_one_client(sock);
169
170     free_gss();
171     free_fqdn();
172     free_ops();
173
174     return 0;
175 }
176
177 int main(int argc, char *argv[]) {
178     int opt;
179     int ret;
180
181     prog = xstrdup(basename(argv[0]));
182     init_log(prog, LOG_PID, LOG_DAEMON, 0);
183
184     while ((opt = getopt_long(argc, argv, "dq", opts, NULL)) != -1) {
185         switch (opt) {
186             case 'd':
187                 detach = 1;
188                 break;
189             case 'q':
190                 log_set_maxprio(LOG_WARNING);
191                 break;
192             case '?':
193                 usage();
194                 break;
195             default:
196                 fatal("error parsing arguments");
197         }
198     }
199
200     configure();
201
202     if (argc != optind)
203         usage();
204
205     ret = master_main();
206
207     free_config();
208     free(prog);
209
210     return ret;
211 }