Add init script for ceod
[mspang/pyceo.git] / src / util.c
1 #define _ATFILE_SOURCE
2 #include <unistd.h>
3 #include <sys/wait.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <stdarg.h>
7 #include <fcntl.h>
8 #include <syslog.h>
9 #include <errno.h>
10 #include <grp.h>
11
12 #include "util.h"
13 #include "strbuf.h"
14
15 static int log_stderr = 1;
16 static int log_maxprio = LOG_DEBUG;
17
18 void init_log(const char *ident, int option, int facility, int lstderr) {
19     openlog(ident, option, facility);
20     log_stderr = lstderr || isatty(STDERR_FILENO);
21 }
22
23 void log_set_maxprio(int prio) {
24     log_maxprio = prio;
25 }
26
27 static void errmsg(int prio, const char *prefix, const char *fmt, va_list args) {
28     struct strbuf msg = STRBUF_INIT;
29
30     strbuf_addf(&msg, "%s: ", prefix);
31     strbuf_vaddf(&msg, fmt, args);
32     strbuf_addch(&msg, '\n');
33
34     syslog(prio, "%s", msg.buf);
35     if (log_stderr && prio <= log_maxprio)
36         fputs(msg.buf, stderr);
37
38     strbuf_release(&msg);
39 }
40
41 static void errmsgpe(int prio, const char *prefix, const char *fmt, va_list args) {
42     struct strbuf msg = STRBUF_INIT;
43
44     strbuf_addf(&msg, "%s: ", prefix);
45     strbuf_vaddf(&msg, fmt, args);
46     strbuf_addf(&msg, ": %s\n", strerror(errno));
47
48     syslog(prio, "%s", msg.buf);
49     if (log_stderr && prio <= log_maxprio)
50         fputs(msg.buf, stderr);
51
52     strbuf_release(&msg);
53 }
54
55 NORETURN static void die(int prio, const char *prefix, const char *msg, va_list args) {
56     errmsg(prio, prefix, msg, args);
57     exit(1);
58 }
59
60 NORETURN static void diepe(int prio, const char *prefix, const char *msg, va_list args) {
61     errmsgpe(prio, prefix, msg, args);
62     exit(1);
63 }
64
65 NORETURN void fatal(const char *msg, ...) {
66     va_list args;
67     va_start(args, msg);
68     die(LOG_CRIT, "fatal", msg, args);
69     va_end(args);
70 }
71
72 void error(const char *msg, ...) {
73     va_list args;
74     va_start(args, msg);
75     errmsg(LOG_ERR, "error", msg, args);
76     va_end(args);
77 }
78
79 void warn(const char *msg, ...) {
80     va_list args;
81     va_start(args, msg);
82     errmsg(LOG_WARNING, "warning", msg, args);
83     va_end(args);
84 }
85
86 void notice(const char *msg, ...) {
87     va_list args;
88     va_start(args, msg);
89     errmsg(LOG_NOTICE, "notice", msg, args);
90     va_end(args);
91 }
92
93 void debug(const char *msg, ...) {
94     va_list args;
95     va_start(args, msg);
96     errmsg(LOG_DEBUG, "debug", msg, args);
97     va_end(args);
98 }
99
100 void logmsg(int priority, const char *msg, ...) {
101     va_list args;
102     va_start(args, msg);
103     vsyslog(priority, msg, args);
104     va_end(args);
105     va_start(args, msg);
106     if (log_stderr && priority <= log_maxprio) {
107         vfprintf(stderr, msg, args);
108         fputc('\n', stderr);
109     }
110     va_end(args);
111 }
112
113 NORETURN void deny(const char *msg, ...) {
114     va_list args;
115     va_start(args, msg);
116     die(LOG_ERR, "denied", msg, args);
117     va_end(args);
118 }
119
120 NORETURN void badconf(const char *msg, ...) {
121     va_list args;
122     va_start(args, msg);
123     die(LOG_CRIT, "configuration error", msg, args);
124     va_end(args);
125 }
126
127 NORETURN void fatalpe(const char *msg, ...) {
128     va_list args;
129     va_start(args, msg);
130     diepe(LOG_CRIT, "fatal", msg, args);
131     va_end(args);
132 }
133
134 void errorpe(const char *msg, ...) {
135     va_list args;
136     va_start(args, msg);
137     errmsgpe(LOG_ERR, "error", msg, args);
138     va_end(args);
139 }
140
141 void warnpe(const char *msg, ...) {
142     va_list args;
143     va_start(args, msg);
144     errmsgpe(LOG_WARNING, "warning", msg, args);
145     va_end(args);
146 }
147
148 int spawnv(const char *path, char *const argv[]) {
149     int pid, status;
150
151     fflush(stdout);
152     fflush(stderr);
153
154     pid = fork();
155     if (pid < 0)
156         fatalpe("fork");
157     else if (pid)
158         waitpid(pid, &status, 0);
159     else
160         exit(execv(path, argv));
161     return status;
162 }
163
164 void full_write(int fd, const void *buf, size_t count) {
165     ssize_t total = 0;
166
167     while (total < count) {
168         ssize_t wcount = write(fd, (char *)buf + total, count - total);
169         if (wcount < 0)
170             fatalpe("write");
171         total += wcount;
172     }
173 }
174
175 int spawnvem(const char *path, char *const *argv, char *const *envp, const struct strbuf *output, struct strbuf *input, int cap_stderr) {
176     int pid, wpid, status;
177     int tochild[2];
178     int fmchild[2];
179
180     if (pipe(tochild))
181         fatalpe("pipe");
182     if (pipe(fmchild))
183         fatalpe("pipe");
184
185     fflush(stdout);
186     fflush(stderr);
187
188     pid = fork();
189     if (pid < 0)
190         fatalpe("fork");
191     if (!pid) {
192         dup2(tochild[0], STDIN_FILENO);
193         dup2(fmchild[1], STDOUT_FILENO);
194         if (cap_stderr)
195             dup2(STDOUT_FILENO, STDERR_FILENO);
196         close(tochild[0]);
197         close(tochild[1]);
198         close(fmchild[0]);
199         close(fmchild[1]);
200         execve(path, argv, envp);
201         fatalpe("execve");
202     } else {
203         close(tochild[0]);
204         close(fmchild[1]);
205         full_write(tochild[1], output->buf, output->len);
206         close(tochild[1]);
207
208         if (input)
209             strbuf_read(input, fmchild[0], 0);
210         close(fmchild[0]);
211     }
212
213     wpid = waitpid(pid, &status, 0);
214     if (wpid < 0)
215         fatalpe("waitpid");
216     else if (wpid != pid)
217         fatal("waitpid is broken");
218
219     if (WIFEXITED(status) && WEXITSTATUS(status))
220         notice("child %s exited with status %d", path, WEXITSTATUS(status));
221     else if (WIFSIGNALED(status))
222         notice("child %s killed by signal %d", path, WTERMSIG(status));
223
224     return status;
225 }
226
227 int spawnv_msg(const char *path, char *const *argv, const struct strbuf *output) {
228     return spawnvem(path, argv, environ, output, NULL, 0);
229 }
230
231 int check_group(char *username, char *group) {
232     struct group *grp = getgrnam(group);
233     char **members;
234
235     if (grp)
236         for (members = grp->gr_mem; *members; members++)
237             if (!strcmp(username, *members))
238                 return 1;
239
240     return 0;
241 }
242
243 FILE *fopenat(DIR *d, const char *path, int flags) {
244     int dfd = dirfd(d);
245     if (dfd < 0)
246         return NULL;
247     int fd = openat(dfd, path, flags);
248     if (fd < 0)
249         return NULL;
250     return fdopen(fd, flags & O_RDWR   ? "r+" :
251                       flags & O_WRONLY ? "w" :
252                                          "r");
253 }
254
255 void make_env(char **envp, ...) {
256     const size_t len = 4096;
257     size_t used = 0;
258     int args = 0;
259     char *buf = xmalloc(len);
260     va_list ap;
261     va_start(ap, envp);
262     char *name, *val;
263
264     while ((name = va_arg(ap, char *))) {
265         val = va_arg(ap, char *);
266         if (!val)
267             continue;
268         int n = snprintf(buf + used, len - used, "%s=%s", name, val);
269         if (n < 0)
270             fatalpe("snprintf");
271         if (n >= len - used)
272             fatal("environment too big");
273
274         envp[args++] = buf + used;
275         used += n + 1;
276     }
277
278     if (!args)
279         free(buf);
280
281     envp[args] = NULL;
282 }
283
284 void free_env(char **envp) {
285     free(*envp);
286 }