chiark / gitweb /
journalctl: use _COMM= match for scripts
[elogind.git] / src / shared / fileio.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <unistd.h>
23 #include "fileio.h"
24 #include "util.h"
25 #include "strv.h"
26
27 int write_string_to_file(FILE *f, const char *line) {
28         errno = 0;
29         fputs(line, f);
30         if (!endswith(line, "\n"))
31                 fputc('\n', f);
32
33         fflush(f);
34
35         if (ferror(f))
36                 return errno ? -errno : -EIO;
37
38         return 0;
39 }
40
41 int write_string_file(const char *fn, const char *line) {
42         _cleanup_fclose_ FILE *f = NULL;
43
44         assert(fn);
45         assert(line);
46
47         f = fopen(fn, "we");
48         if (!f)
49                 return -errno;
50
51         return write_string_to_file(f, line);
52 }
53
54 int write_string_file_atomic(const char *fn, const char *line) {
55         _cleanup_fclose_ FILE *f = NULL;
56         _cleanup_free_ char *p = NULL;
57         int r;
58
59         assert(fn);
60         assert(line);
61
62         r = fopen_temporary(fn, &f, &p);
63         if (r < 0)
64                 return r;
65
66         fchmod_umask(fileno(f), 0644);
67
68         errno = 0;
69         fputs(line, f);
70         if (!endswith(line, "\n"))
71                 fputc('\n', f);
72
73         fflush(f);
74
75         if (ferror(f))
76                 r = errno ? -errno : -EIO;
77         else {
78                 if (rename(p, fn) < 0)
79                         r = -errno;
80                 else
81                         r = 0;
82         }
83
84         if (r < 0)
85                 unlink(p);
86
87         return r;
88 }
89
90 int read_one_line_file(const char *fn, char **line) {
91         _cleanup_fclose_ FILE *f = NULL;
92         char t[LINE_MAX], *c;
93
94         assert(fn);
95         assert(line);
96
97         f = fopen(fn, "re");
98         if (!f)
99                 return -errno;
100
101         if (!fgets(t, sizeof(t), f)) {
102
103                 if (ferror(f))
104                         return errno ? -errno : -EIO;
105
106                 t[0] = 0;
107         }
108
109         c = strdup(t);
110         if (!c)
111                 return -ENOMEM;
112         truncate_nl(c);
113
114         *line = c;
115         return 0;
116 }
117
118 int read_full_file(const char *fn, char **contents, size_t *size) {
119         _cleanup_fclose_ FILE *f = NULL;
120         size_t n, l;
121         _cleanup_free_ char *buf = NULL;
122         struct stat st;
123
124         assert(fn);
125         assert(contents);
126
127         f = fopen(fn, "re");
128         if (!f)
129                 return -errno;
130
131         if (fstat(fileno(f), &st) < 0)
132                 return -errno;
133
134         /* Safety check */
135         if (st.st_size > 4*1024*1024)
136                 return -E2BIG;
137
138         n = st.st_size > 0 ? st.st_size : LINE_MAX;
139         l = 0;
140
141         for (;;) {
142                 char *t;
143                 size_t k;
144
145                 t = realloc(buf, n+1);
146                 if (!t)
147                         return -ENOMEM;
148
149                 buf = t;
150                 k = fread(buf + l, 1, n - l, f);
151
152                 if (k <= 0) {
153                         if (ferror(f))
154                                 return -errno;
155
156                         break;
157                 }
158
159                 l += k;
160                 n *= 2;
161
162                 /* Safety check */
163                 if (n > 4*1024*1024)
164                         return -E2BIG;
165         }
166
167         buf[l] = 0;
168         *contents = buf;
169         buf = NULL;
170
171         if (size)
172                 *size = l;
173
174         return 0;
175 }
176
177 static int parse_env_file_internal(
178                 const char *fname,
179                 const char *newline,
180                 int (*push) (const char *key, char *value, void *userdata),
181                 void *userdata) {
182
183         _cleanup_free_ char *contents = NULL, *key = NULL;
184         size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1;
185         char *p, *value = NULL;
186         int r;
187
188         enum {
189                 PRE_KEY,
190                 KEY,
191                 PRE_VALUE,
192                 VALUE,
193                 VALUE_ESCAPE,
194                 SINGLE_QUOTE_VALUE,
195                 SINGLE_QUOTE_VALUE_ESCAPE,
196                 DOUBLE_QUOTE_VALUE,
197                 DOUBLE_QUOTE_VALUE_ESCAPE,
198                 COMMENT,
199                 COMMENT_ESCAPE
200         } state = PRE_KEY;
201
202         assert(fname);
203         assert(newline);
204
205         r = read_full_file(fname, &contents, NULL);
206         if (r < 0)
207                 return r;
208
209         for (p = contents; *p; p++) {
210                 char c = *p;
211
212                 switch (state) {
213
214                 case PRE_KEY:
215                         if (strchr(COMMENTS, c))
216                                 state = COMMENT;
217                         else if (!strchr(WHITESPACE, c)) {
218                                 state = KEY;
219                                 last_key_whitespace = (size_t) -1;
220
221                                 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
222                                         r = -ENOMEM;
223                                         goto fail;
224                                 }
225
226                                 key[n_key++] = c;
227                         }
228                         break;
229
230                 case KEY:
231                         if (strchr(newline, c)) {
232                                 state = PRE_KEY;
233                                 n_key = 0;
234                         } else if (c == '=') {
235                                 state = PRE_VALUE;
236                                 last_value_whitespace = (size_t) -1;
237                         } else {
238                                 if (!strchr(WHITESPACE, c))
239                                         last_key_whitespace = (size_t) -1;
240                                 else if (last_key_whitespace == (size_t) -1)
241                                          last_key_whitespace = n_key;
242
243                                 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
244                                         r = -ENOMEM;
245                                         goto fail;
246                                 }
247
248                                 key[n_key++] = c;
249                         }
250
251                         break;
252
253                 case PRE_VALUE:
254                         if (strchr(newline, c)) {
255                                 state = PRE_KEY;
256                                 key[n_key] = 0;
257
258                                 if (value)
259                                         value[n_value] = 0;
260
261                                 /* strip trailing whitespace from key */
262                                 if (last_key_whitespace != (size_t) -1)
263                                         key[last_key_whitespace] = 0;
264
265                                 r = push(key, value, userdata);
266                                 if (r < 0)
267                                         goto fail;
268
269                                 n_key = 0;
270                                 value = NULL;
271                                 value_alloc = n_value = 0;
272
273                         } else if (c == '\'')
274                                 state = SINGLE_QUOTE_VALUE;
275                         else if (c == '\"')
276                                 state = DOUBLE_QUOTE_VALUE;
277                         else if (c == '\\')
278                                 state = VALUE_ESCAPE;
279                         else if (!strchr(WHITESPACE, c)) {
280                                 state = VALUE;
281
282                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
283                                         r = -ENOMEM;
284                                         goto fail;
285                                 }
286
287                                 value[n_value++] = c;
288                         }
289
290                         break;
291
292                 case VALUE:
293                         if (strchr(newline, c)) {
294                                 state = PRE_KEY;
295
296                                 key[n_key] = 0;
297
298                                 if (value)
299                                         value[n_value] = 0;
300
301                                 /* Chomp off trailing whitespace from value */
302                                 if (last_value_whitespace != (size_t) -1)
303                                         value[last_value_whitespace] = 0;
304
305                                 /* strip trailing whitespace from key */
306                                 if (last_key_whitespace != (size_t) -1)
307                                         key[last_key_whitespace] = 0;
308
309                                 r = push(key, value, userdata);
310                                 if (r < 0)
311                                         goto fail;
312
313                                 n_key = 0;
314                                 value = NULL;
315                                 value_alloc = n_value = 0;
316
317                         } else if (c == '\\') {
318                                 state = VALUE_ESCAPE;
319                                 last_value_whitespace = (size_t) -1;
320                         } else {
321                                 if (!strchr(WHITESPACE, c))
322                                         last_value_whitespace = (size_t) -1;
323                                 else if (last_value_whitespace == (size_t) -1)
324                                         last_value_whitespace = n_value;
325
326                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
327                                         r = -ENOMEM;
328                                         goto fail;
329                                 }
330
331                                 value[n_value++] = c;
332                         }
333
334                         break;
335
336                 case VALUE_ESCAPE:
337                         state = VALUE;
338
339                         if (!strchr(newline, c)) {
340                                 /* Escaped newlines we eat up entirely */
341                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
342                                         r = -ENOMEM;
343                                         goto fail;
344                                 }
345
346                                 value[n_value++] = c;
347                         }
348                         break;
349
350                 case SINGLE_QUOTE_VALUE:
351                         if (c == '\'')
352                                 state = PRE_VALUE;
353                         else if (c == '\\')
354                                 state = SINGLE_QUOTE_VALUE_ESCAPE;
355                         else {
356                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
357                                         r = -ENOMEM;
358                                         goto fail;
359                                 }
360
361                                 value[n_value++] = c;
362                         }
363
364                         break;
365
366                 case SINGLE_QUOTE_VALUE_ESCAPE:
367                         state = SINGLE_QUOTE_VALUE;
368
369                         if (!strchr(newline, c)) {
370                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
371                                         r = -ENOMEM;
372                                         goto fail;
373                                 }
374
375                                 value[n_value++] = c;
376                         }
377                         break;
378
379                 case DOUBLE_QUOTE_VALUE:
380                         if (c == '\"')
381                                 state = PRE_VALUE;
382                         else if (c == '\\')
383                                 state = DOUBLE_QUOTE_VALUE_ESCAPE;
384                         else {
385                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
386                                         r = -ENOMEM;
387                                         goto fail;
388                                 }
389
390                                 value[n_value++] = c;
391                         }
392
393                         break;
394
395                 case DOUBLE_QUOTE_VALUE_ESCAPE:
396                         state = DOUBLE_QUOTE_VALUE;
397
398                         if (!strchr(newline, c)) {
399                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
400                                         r = -ENOMEM;
401                                         goto fail;
402                                 }
403
404                                 value[n_value++] = c;
405                         }
406                         break;
407
408                 case COMMENT:
409                         if (c == '\\')
410                                 state = COMMENT_ESCAPE;
411                         else if (strchr(newline, c))
412                                 state = PRE_KEY;
413                         break;
414
415                 case COMMENT_ESCAPE:
416                         state = COMMENT;
417                         break;
418                 }
419         }
420
421         if (state == PRE_VALUE ||
422             state == VALUE ||
423             state == VALUE_ESCAPE ||
424             state == SINGLE_QUOTE_VALUE ||
425             state == SINGLE_QUOTE_VALUE_ESCAPE ||
426             state == DOUBLE_QUOTE_VALUE ||
427             state == DOUBLE_QUOTE_VALUE_ESCAPE) {
428
429                 key[n_key] = 0;
430
431                 if (value)
432                         value[n_value] = 0;
433
434                 if (state == VALUE)
435                         if (last_value_whitespace != (size_t) -1)
436                                 value[last_value_whitespace] = 0;
437
438                 /* strip trailing whitespace from key */
439                 if (last_key_whitespace != (size_t) -1)
440                         key[last_key_whitespace] = 0;
441
442                 r = push(key, value, userdata);
443                 if (r < 0)
444                         goto fail;
445         }
446
447         return 0;
448
449 fail:
450         free(value);
451         return r;
452 }
453
454 static int parse_env_file_push(const char *key, char *value, void *userdata) {
455         const char *k;
456         va_list* ap = (va_list*) userdata;
457         va_list aq;
458
459         va_copy(aq, *ap);
460
461         while ((k = va_arg(aq, const char *))) {
462                 char **v;
463
464                 v = va_arg(aq, char **);
465
466                 if (streq(key, k)) {
467                         va_end(aq);
468                         free(*v);
469                         *v = value;
470                         return 1;
471                 }
472         }
473
474         va_end(aq);
475
476         free(value);
477         return 0;
478 }
479
480 int parse_env_file(
481                 const char *fname,
482                 const char *newline, ...) {
483
484         va_list ap;
485         int r;
486
487         if (!newline)
488                 newline = NEWLINE;
489
490         va_start(ap, newline);
491         r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap);
492         va_end(ap);
493
494         return r;
495 }
496
497 static int load_env_file_push(const char *key, char *value, void *userdata) {
498         char ***m = userdata;
499         char *p;
500         int r;
501
502         p = strjoin(key, "=", strempty(value), NULL);
503         if (!p)
504                 return -ENOMEM;
505
506         r = strv_push(m, p);
507         if (r < 0) {
508                 free(p);
509                 return r;
510         }
511
512         free(value);
513         return 0;
514 }
515
516 int load_env_file(const char *fname, const char *newline, char ***rl) {
517         char **m = NULL;
518         int r;
519
520         if (!newline)
521                 newline = NEWLINE;
522
523         r = parse_env_file_internal(fname, newline, load_env_file_push, &m);
524         if (r < 0) {
525                 strv_free(m);
526                 return r;
527         }
528
529         *rl = m;
530         return 0;
531 }
532
533 static void write_env_var(FILE *f, const char *v) {
534         const char *p;
535
536         p = strchr(v, '=');
537         if (!p) {
538                 /* Fallback */
539                 fputs(v, f);
540                 fputc('\n', f);
541                 return;
542         }
543
544         p++;
545         fwrite(v, 1, p-v, f);
546
547         if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\`$")) {
548                 fputc('\"', f);
549
550                 for (; *p; p++) {
551                         if (strchr("\'\"\\`$", *p))
552                                 fputc('\\', f);
553
554                         fputc(*p, f);
555                 }
556
557                 fputc('\"', f);
558         } else
559                 fputs(p, f);
560
561         fputc('\n', f);
562 }
563
564 int write_env_file(const char *fname, char **l) {
565         char **i;
566         _cleanup_free_ char *p = NULL;
567         _cleanup_fclose_ FILE *f = NULL;
568         int r;
569
570         r = fopen_temporary(fname, &f, &p);
571         if (r < 0)
572                 return r;
573
574         fchmod_umask(fileno(f), 0644);
575
576         errno = 0;
577         STRV_FOREACH(i, l)
578                 write_env_var(f, *i);
579
580         fflush(f);
581
582         if (ferror(f))
583                 r = errno ? -errno : -EIO;
584         else {
585                 if (rename(p, fname) < 0)
586                         r = -errno;
587                 else
588                         r = 0;
589         }
590
591         if (r < 0)
592                 unlink(p);
593
594         return r;
595 }
596
597 int executable_is_script(const char *path, char **interpreter) {
598         int r;
599         char _cleanup_free_ *line = NULL;
600         int len;
601         char *ans;
602
603         assert(path);
604
605         r = read_one_line_file(path, &line);
606         if (r < 0)
607                 return r;
608
609         if (!startswith(line, "#!"))
610                 return 0;
611
612         ans = strstrip(line + 2);
613         len = strcspn(ans, " \t");
614
615         if (len == 0)
616                 return 0;
617
618         ans = strndup(ans, len);
619         if (!ans)
620                 return -ENOMEM;
621
622         *interpreter = ans;
623         return 1;
624 }