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