chiark / gitweb /
Never call qsort on potentially NULL arrays
[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 #include "utf8.h"
27 #include "ctype.h"
28
29 int write_string_to_file(FILE *f, const char *line) {
30         errno = 0;
31         fputs(line, f);
32         if (!endswith(line, "\n"))
33                 fputc('\n', f);
34
35         fflush(f);
36
37         if (ferror(f))
38                 return errno ? -errno : -EIO;
39
40         return 0;
41 }
42
43 int write_string_file(const char *fn, const char *line) {
44         _cleanup_fclose_ FILE *f = NULL;
45
46         assert(fn);
47         assert(line);
48
49         f = fopen(fn, "we");
50         if (!f)
51                 return -errno;
52
53         return write_string_to_file(f, line);
54 }
55
56 int write_string_file_atomic(const char *fn, const char *line) {
57         _cleanup_fclose_ FILE *f = NULL;
58         _cleanup_free_ char *p = NULL;
59         int r;
60
61         assert(fn);
62         assert(line);
63
64         r = fopen_temporary(fn, &f, &p);
65         if (r < 0)
66                 return r;
67
68         fchmod_umask(fileno(f), 0644);
69
70         errno = 0;
71         fputs(line, f);
72         if (!endswith(line, "\n"))
73                 fputc('\n', f);
74
75         fflush(f);
76
77         if (ferror(f))
78                 r = errno ? -errno : -EIO;
79         else {
80                 if (rename(p, fn) < 0)
81                         r = -errno;
82                 else
83                         r = 0;
84         }
85
86         if (r < 0)
87                 unlink(p);
88
89         return r;
90 }
91
92 int read_one_line_file(const char *fn, char **line) {
93         _cleanup_fclose_ FILE *f = NULL;
94         char t[LINE_MAX], *c;
95
96         assert(fn);
97         assert(line);
98
99         f = fopen(fn, "re");
100         if (!f)
101                 return -errno;
102
103         if (!fgets(t, sizeof(t), f)) {
104
105                 if (ferror(f))
106                         return errno ? -errno : -EIO;
107
108                 t[0] = 0;
109         }
110
111         c = strdup(t);
112         if (!c)
113                 return -ENOMEM;
114         truncate_nl(c);
115
116         *line = c;
117         return 0;
118 }
119
120 int read_full_file(const char *fn, char **contents, size_t *size) {
121         _cleanup_fclose_ FILE *f = NULL;
122         size_t n, l;
123         _cleanup_free_ char *buf = NULL;
124         struct stat st;
125
126         assert(fn);
127         assert(contents);
128
129         f = fopen(fn, "re");
130         if (!f)
131                 return -errno;
132
133         if (fstat(fileno(f), &st) < 0)
134                 return -errno;
135
136         /* Safety check */
137         if (st.st_size > 4*1024*1024)
138                 return -E2BIG;
139
140         n = st.st_size > 0 ? st.st_size : LINE_MAX;
141         l = 0;
142
143         for (;;) {
144                 char *t;
145                 size_t k;
146
147                 t = realloc(buf, n+1);
148                 if (!t)
149                         return -ENOMEM;
150
151                 buf = t;
152                 k = fread(buf + l, 1, n - l, f);
153
154                 if (k <= 0) {
155                         if (ferror(f))
156                                 return -errno;
157
158                         break;
159                 }
160
161                 l += k;
162                 n *= 2;
163
164                 /* Safety check */
165                 if (n > 4*1024*1024)
166                         return -E2BIG;
167         }
168
169         buf[l] = 0;
170         *contents = buf;
171         buf = NULL;
172
173         if (size)
174                 *size = l;
175
176         return 0;
177 }
178
179 static int parse_env_file_internal(
180                 const char *fname,
181                 const char *newline,
182                 int (*push) (const char *filename, unsigned line,
183                              const char *key, char *value, void *userdata),
184                 void *userdata) {
185
186         _cleanup_free_ char *contents = NULL, *key = NULL;
187         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;
188         char *p, *value = NULL;
189         int r;
190         unsigned line = 1;
191
192         enum {
193                 PRE_KEY,
194                 KEY,
195                 PRE_VALUE,
196                 VALUE,
197                 VALUE_ESCAPE,
198                 SINGLE_QUOTE_VALUE,
199                 SINGLE_QUOTE_VALUE_ESCAPE,
200                 DOUBLE_QUOTE_VALUE,
201                 DOUBLE_QUOTE_VALUE_ESCAPE,
202                 COMMENT,
203                 COMMENT_ESCAPE
204         } state = PRE_KEY;
205
206         assert(fname);
207         assert(newline);
208
209         r = read_full_file(fname, &contents, NULL);
210         if (r < 0)
211                 return r;
212
213         for (p = contents; *p; p++) {
214                 char c = *p;
215
216                 switch (state) {
217
218                 case PRE_KEY:
219                         if (strchr(COMMENTS, c))
220                                 state = COMMENT;
221                         else if (!strchr(WHITESPACE, c)) {
222                                 state = KEY;
223                                 last_key_whitespace = (size_t) -1;
224
225                                 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
226                                         r = -ENOMEM;
227                                         goto fail;
228                                 }
229
230                                 key[n_key++] = c;
231                         }
232                         break;
233
234                 case KEY:
235                         if (strchr(newline, c)) {
236                                 state = PRE_KEY;
237                                 line ++;
238                                 n_key = 0;
239                         } else if (c == '=') {
240                                 state = PRE_VALUE;
241                                 last_value_whitespace = (size_t) -1;
242                         } else {
243                                 if (!strchr(WHITESPACE, c))
244                                         last_key_whitespace = (size_t) -1;
245                                 else if (last_key_whitespace == (size_t) -1)
246                                          last_key_whitespace = n_key;
247
248                                 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
249                                         r = -ENOMEM;
250                                         goto fail;
251                                 }
252
253                                 key[n_key++] = c;
254                         }
255
256                         break;
257
258                 case PRE_VALUE:
259                         if (strchr(newline, c)) {
260                                 state = PRE_KEY;
261                                 line ++;
262                                 key[n_key] = 0;
263
264                                 if (value)
265                                         value[n_value] = 0;
266
267                                 /* strip trailing whitespace from key */
268                                 if (last_key_whitespace != (size_t) -1)
269                                         key[last_key_whitespace] = 0;
270
271                                 r = push(fname, line, key, value, userdata);
272                                 if (r < 0)
273                                         goto fail;
274
275                                 n_key = 0;
276                                 value = NULL;
277                                 value_alloc = n_value = 0;
278
279                         } else if (c == '\'')
280                                 state = SINGLE_QUOTE_VALUE;
281                         else if (c == '\"')
282                                 state = DOUBLE_QUOTE_VALUE;
283                         else if (c == '\\')
284                                 state = VALUE_ESCAPE;
285                         else if (!strchr(WHITESPACE, c)) {
286                                 state = VALUE;
287
288                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
289                                         r = -ENOMEM;
290                                         goto fail;
291                                 }
292
293                                 value[n_value++] = c;
294                         }
295
296                         break;
297
298                 case VALUE:
299                         if (strchr(newline, c)) {
300                                 state = PRE_KEY;
301                                 line ++;
302
303                                 key[n_key] = 0;
304
305                                 if (value)
306                                         value[n_value] = 0;
307
308                                 /* Chomp off trailing whitespace from value */
309                                 if (last_value_whitespace != (size_t) -1)
310                                         value[last_value_whitespace] = 0;
311
312                                 /* strip trailing whitespace from key */
313                                 if (last_key_whitespace != (size_t) -1)
314                                         key[last_key_whitespace] = 0;
315
316                                 r = push(fname, line, key, value, userdata);
317                                 if (r < 0)
318                                         goto fail;
319
320                                 n_key = 0;
321                                 value = NULL;
322                                 value_alloc = n_value = 0;
323
324                         } else if (c == '\\') {
325                                 state = VALUE_ESCAPE;
326                                 last_value_whitespace = (size_t) -1;
327                         } else {
328                                 if (!strchr(WHITESPACE, c))
329                                         last_value_whitespace = (size_t) -1;
330                                 else if (last_value_whitespace == (size_t) -1)
331                                         last_value_whitespace = n_value;
332
333                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
334                                         r = -ENOMEM;
335                                         goto fail;
336                                 }
337
338                                 value[n_value++] = c;
339                         }
340
341                         break;
342
343                 case VALUE_ESCAPE:
344                         state = VALUE;
345
346                         if (!strchr(newline, c)) {
347                                 /* Escaped newlines we eat up entirely */
348                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
349                                         r = -ENOMEM;
350                                         goto fail;
351                                 }
352
353                                 value[n_value++] = c;
354                         }
355                         break;
356
357                 case SINGLE_QUOTE_VALUE:
358                         if (c == '\'')
359                                 state = PRE_VALUE;
360                         else if (c == '\\')
361                                 state = SINGLE_QUOTE_VALUE_ESCAPE;
362                         else {
363                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
364                                         r = -ENOMEM;
365                                         goto fail;
366                                 }
367
368                                 value[n_value++] = c;
369                         }
370
371                         break;
372
373                 case SINGLE_QUOTE_VALUE_ESCAPE:
374                         state = SINGLE_QUOTE_VALUE;
375
376                         if (!strchr(newline, c)) {
377                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
378                                         r = -ENOMEM;
379                                         goto fail;
380                                 }
381
382                                 value[n_value++] = c;
383                         }
384                         break;
385
386                 case DOUBLE_QUOTE_VALUE:
387                         if (c == '\"')
388                                 state = PRE_VALUE;
389                         else if (c == '\\')
390                                 state = DOUBLE_QUOTE_VALUE_ESCAPE;
391                         else {
392                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
393                                         r = -ENOMEM;
394                                         goto fail;
395                                 }
396
397                                 value[n_value++] = c;
398                         }
399
400                         break;
401
402                 case DOUBLE_QUOTE_VALUE_ESCAPE:
403                         state = DOUBLE_QUOTE_VALUE;
404
405                         if (!strchr(newline, c)) {
406                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
407                                         r = -ENOMEM;
408                                         goto fail;
409                                 }
410
411                                 value[n_value++] = c;
412                         }
413                         break;
414
415                 case COMMENT:
416                         if (c == '\\')
417                                 state = COMMENT_ESCAPE;
418                         else if (strchr(newline, c)) {
419                                 state = PRE_KEY;
420                                 line ++;
421                         }
422                         break;
423
424                 case COMMENT_ESCAPE:
425                         state = COMMENT;
426                         break;
427                 }
428         }
429
430         if (state == PRE_VALUE ||
431             state == VALUE ||
432             state == VALUE_ESCAPE ||
433             state == SINGLE_QUOTE_VALUE ||
434             state == SINGLE_QUOTE_VALUE_ESCAPE ||
435             state == DOUBLE_QUOTE_VALUE ||
436             state == DOUBLE_QUOTE_VALUE_ESCAPE) {
437
438                 key[n_key] = 0;
439
440                 if (value)
441                         value[n_value] = 0;
442
443                 if (state == VALUE)
444                         if (last_value_whitespace != (size_t) -1)
445                                 value[last_value_whitespace] = 0;
446
447                 /* strip trailing whitespace from key */
448                 if (last_key_whitespace != (size_t) -1)
449                         key[last_key_whitespace] = 0;
450
451                 r = push(fname, line, key, value, userdata);
452                 if (r < 0)
453                         goto fail;
454         }
455
456         return 0;
457
458 fail:
459         free(value);
460         return r;
461 }
462
463 static int parse_env_file_push(const char *filename, unsigned line,
464                                const char *key, char *value, void *userdata) {
465         assert(utf8_is_valid(key));
466
467         if (value && !utf8_is_valid(value))
468                 /* FIXME: filter UTF-8 */
469                 log_error("%s:%u: invalid UTF-8 for key %s: '%s', ignoring.",
470                           filename, line, key, value);
471         else {
472                 const char *k;
473                 va_list* ap = (va_list*) userdata;
474                 va_list aq;
475
476                 va_copy(aq, *ap);
477
478                 while ((k = va_arg(aq, const char *))) {
479                         char **v;
480
481                         v = va_arg(aq, char **);
482
483                         if (streq(key, k)) {
484                                 va_end(aq);
485                                 free(*v);
486                                 *v = value;
487                                 return 1;
488                         }
489                 }
490
491                 va_end(aq);
492         }
493
494         free(value);
495         return 0;
496 }
497
498 int parse_env_file(
499                 const char *fname,
500                 const char *newline, ...) {
501
502         va_list ap;
503         int r;
504
505         if (!newline)
506                 newline = NEWLINE;
507
508         va_start(ap, newline);
509         r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap);
510         va_end(ap);
511
512         return r;
513 }
514
515 static int load_env_file_push(const char *filename, unsigned line,
516                               const char *key, char *value, void *userdata) {
517         assert(utf8_is_valid(key));
518
519         if (value && !utf8_is_valid(value))
520                 /* FIXME: filter UTF-8 */
521                 log_error("%s:%u: invalid UTF-8 for key %s: '%s', ignoring.",
522                           filename, line, key, value);
523         else {
524                 char ***m = userdata;
525                 char *p;
526                 int r;
527
528                 p = strjoin(key, "=", strempty(value), NULL);
529                 if (!p)
530                         return -ENOMEM;
531
532                 r = strv_push(m, p);
533                 if (r < 0) {
534                         free(p);
535                         return r;
536                 }
537         }
538
539         free(value);
540         return 0;
541 }
542
543 int load_env_file(const char *fname, const char *newline, char ***rl) {
544         char **m = NULL;
545         int r;
546
547         if (!newline)
548                 newline = NEWLINE;
549
550         r = parse_env_file_internal(fname, newline, load_env_file_push, &m);
551         if (r < 0) {
552                 strv_free(m);
553                 return r;
554         }
555
556         *rl = m;
557         return 0;
558 }
559
560 static void write_env_var(FILE *f, const char *v) {
561         const char *p;
562
563         p = strchr(v, '=');
564         if (!p) {
565                 /* Fallback */
566                 fputs(v, f);
567                 fputc('\n', f);
568                 return;
569         }
570
571         p++;
572         fwrite(v, 1, p-v, f);
573
574         if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\`$")) {
575                 fputc('\"', f);
576
577                 for (; *p; p++) {
578                         if (strchr("\'\"\\`$", *p))
579                                 fputc('\\', f);
580
581                         fputc(*p, f);
582                 }
583
584                 fputc('\"', f);
585         } else
586                 fputs(p, f);
587
588         fputc('\n', f);
589 }
590
591 int write_env_file(const char *fname, char **l) {
592         char **i;
593         _cleanup_free_ char *p = NULL;
594         _cleanup_fclose_ FILE *f = NULL;
595         int r;
596
597         r = fopen_temporary(fname, &f, &p);
598         if (r < 0)
599                 return r;
600
601         fchmod_umask(fileno(f), 0644);
602
603         errno = 0;
604         STRV_FOREACH(i, l)
605                 write_env_var(f, *i);
606
607         fflush(f);
608
609         if (ferror(f))
610                 r = errno ? -errno : -EIO;
611         else {
612                 if (rename(p, fname) < 0)
613                         r = -errno;
614                 else
615                         r = 0;
616         }
617
618         if (r < 0)
619                 unlink(p);
620
621         return r;
622 }
623
624 int executable_is_script(const char *path, char **interpreter) {
625         int r;
626         char _cleanup_free_ *line = NULL;
627         int len;
628         char *ans;
629
630         assert(path);
631
632         r = read_one_line_file(path, &line);
633         if (r < 0)
634                 return r;
635
636         if (!startswith(line, "#!"))
637                 return 0;
638
639         ans = strstrip(line + 2);
640         len = strcspn(ans, " \t");
641
642         if (len == 0)
643                 return 0;
644
645         ans = strndup(ans, len);
646         if (!ans)
647                 return -ENOMEM;
648
649         *interpreter = ans;
650         return 1;
651 }
652
653 /**
654  * Retrieve one field from a file like /proc/self/status.  pattern
655  * should start with '\n' and end with a ':'. Whitespace and zeros
656  * after the ':' will be skipped. field must be freed afterwards.
657  */
658 int get_status_field(const char *filename, const char *pattern, char **field) {
659         _cleanup_free_ char *status = NULL;
660         char *t;
661         size_t len;
662         int r;
663
664         assert(filename);
665         assert(pattern);
666         assert(field);
667
668         r = read_full_file(filename, &status, NULL);
669         if (r < 0)
670                 return r;
671
672         t = strstr(status, pattern);
673         if (!t)
674                 return -ENOENT;
675
676         t += strlen(pattern);
677         if (*t) {
678                 t += strspn(t, " \t");
679
680                 /* Also skip zeros, because when this is used for
681                  * capabilities, we don't want the zeros. This way the
682                  * same capability set always maps to the same string,
683                  * irrespective of the total capability set size. For
684                  * other numbers it shouldn't matter. */
685                 t += strspn(t, "0");
686                 /* Back off one char if there's nothing but whitespace
687                    and zeros */
688                 if (!*t || isspace(*t))
689                         t --;
690         }
691
692         len = strcspn(t, WHITESPACE);
693
694         *field = strndup(t, len);
695         if (!*field)
696                 return -ENOMEM;
697
698         return 0;
699 }