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