chiark / gitweb /
systemctl: drop usage of "internally loaded modules"
[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_file(const char *fn, const char *line) {
28         _cleanup_fclose_ FILE *f = NULL;
29
30         assert(fn);
31         assert(line);
32
33         f = fopen(fn, "we");
34         if (!f)
35                 return -errno;
36
37         errno = 0;
38         fputs(line, f);
39         if (!endswith(line, "\n"))
40                 fputc('\n', f);
41
42         fflush(f);
43
44         if (ferror(f))
45                 return errno ? -errno : -EIO;
46
47         return 0;
48 }
49
50 int write_string_file_atomic(const char *fn, const char *line) {
51         _cleanup_fclose_ FILE *f = NULL;
52         _cleanup_free_ char *p = NULL;
53         int r;
54
55         assert(fn);
56         assert(line);
57
58         r = fopen_temporary(fn, &f, &p);
59         if (r < 0)
60                 return r;
61
62         fchmod_umask(fileno(f), 0644);
63
64         errno = 0;
65         fputs(line, f);
66         if (!endswith(line, "\n"))
67                 fputc('\n', f);
68
69         fflush(f);
70
71         if (ferror(f))
72                 r = errno ? -errno : -EIO;
73         else {
74                 if (rename(p, fn) < 0)
75                         r = -errno;
76                 else
77                         r = 0;
78         }
79
80         if (r < 0)
81                 unlink(p);
82
83         return r;
84 }
85
86 int read_one_line_file(const char *fn, char **line) {
87         _cleanup_fclose_ FILE *f = NULL;
88         char t[LINE_MAX], *c;
89
90         assert(fn);
91         assert(line);
92
93         f = fopen(fn, "re");
94         if (!f)
95                 return -errno;
96
97         if (!fgets(t, sizeof(t), f)) {
98
99                 if (ferror(f))
100                         return errno ? -errno : -EIO;
101
102                 t[0] = 0;
103         }
104
105         c = strdup(t);
106         if (!c)
107                 return -ENOMEM;
108         truncate_nl(c);
109
110         *line = c;
111         return 0;
112 }
113
114 int read_full_file(const char *fn, char **contents, size_t *size) {
115         _cleanup_fclose_ FILE *f = NULL;
116         size_t n, l;
117         _cleanup_free_ char *buf = NULL;
118         struct stat st;
119
120         assert(fn);
121         assert(contents);
122
123         f = fopen(fn, "re");
124         if (!f)
125                 return -errno;
126
127         if (fstat(fileno(f), &st) < 0)
128                 return -errno;
129
130         /* Safety check */
131         if (st.st_size > 4*1024*1024)
132                 return -E2BIG;
133
134         n = st.st_size > 0 ? st.st_size : LINE_MAX;
135         l = 0;
136
137         for (;;) {
138                 char *t;
139                 size_t k;
140
141                 t = realloc(buf, n+1);
142                 if (!t)
143                         return -ENOMEM;
144
145                 buf = t;
146                 k = fread(buf + l, 1, n - l, f);
147
148                 if (k <= 0) {
149                         if (ferror(f))
150                                 return -errno;
151
152                         break;
153                 }
154
155                 l += k;
156                 n *= 2;
157
158                 /* Safety check */
159                 if (n > 4*1024*1024)
160                         return -E2BIG;
161         }
162
163         buf[l] = 0;
164         *contents = buf;
165         buf = NULL;
166
167         if (size)
168                 *size = l;
169
170         return 0;
171 }
172
173 static int parse_env_file_internal(
174                 const char *fname,
175                 const char *newline,
176                 int (*push) (const char *key, char *value, void *userdata),
177                 void *userdata) {
178
179         _cleanup_free_ char *contents = NULL, *key = NULL;
180         size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_whitespace = (size_t) -1;
181         char *p, *value = NULL;
182         int r;
183
184         enum {
185                 PRE_KEY,
186                 KEY,
187                 PRE_VALUE,
188                 VALUE,
189                 VALUE_ESCAPE,
190                 SINGLE_QUOTE_VALUE,
191                 SINGLE_QUOTE_VALUE_ESCAPE,
192                 DOUBLE_QUOTE_VALUE,
193                 DOUBLE_QUOTE_VALUE_ESCAPE,
194                 COMMENT,
195                 COMMENT_ESCAPE
196         } state = PRE_KEY;
197
198         assert(fname);
199         assert(newline);
200
201         r = read_full_file(fname, &contents, NULL);
202         if (r < 0)
203                 return r;
204
205         for (p = contents; *p; p++) {
206                 char c = *p;
207
208                 switch (state) {
209
210                 case PRE_KEY:
211                         if (strchr(COMMENTS, c))
212                                 state = COMMENT;
213                         else if (!strchr(WHITESPACE, c)) {
214                                 state = KEY;
215                                 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
216                                         r = -ENOMEM;
217                                         goto fail;
218                                 }
219
220                                 key[n_key++] = c;
221                         }
222                         break;
223
224                 case KEY:
225                         if (strchr(newline, c)) {
226                                 state = PRE_KEY;
227                                 n_key = 0;
228                         } else if (c == '=')
229                                 state = PRE_VALUE;
230                         else {
231                                 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
232                                         r = -ENOMEM;
233                                         goto fail;
234                                 }
235
236                                 key[n_key++] = c;
237                         }
238
239                         break;
240
241                 case PRE_VALUE:
242                         if (strchr(newline, c) || strchr(COMMENTS, c)) {
243                                 state = PRE_KEY;
244                                 key[n_key] = 0;
245
246                                 if (value)
247                                         value[n_value] = 0;
248
249                                 /* strip trailing whitespace from key */
250                                 while(strchr(WHITESPACE, key[--n_key]))
251                                         key[n_key]=0;
252
253                                 r = push(key, value, userdata);
254                                 if (r < 0)
255                                         goto fail;
256
257                                 n_key = 0;
258                                 value = NULL;
259                                 value_alloc = n_value = 0;
260                         } else if (c == '\'')
261                                 state = SINGLE_QUOTE_VALUE;
262                         else if (c == '\"')
263                                 state = DOUBLE_QUOTE_VALUE;
264                         else if (c == '\\')
265                                 state = VALUE_ESCAPE;
266                         else if (!strchr(WHITESPACE, c)) {
267                                 state = VALUE;
268
269                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
270                                         r = -ENOMEM;
271                                         goto fail;
272                                 }
273
274                                 value[n_value++] = c;
275                         }
276
277                         break;
278
279                 case VALUE:
280                         if (strchr(newline, c)) {
281                                 state = PRE_KEY;
282                                 key[n_key] = 0;
283
284                                 if (value)
285                                         value[n_value] = 0;
286
287                                 /* Chomp off trailing whitespace */
288                                 if (last_whitespace != (size_t) -1)
289                                         value[last_whitespace] = 0;
290
291                                 /* strip trailing whitespace from key */
292                                 while(strchr(WHITESPACE, key[--n_key]))
293                                         key[n_key]=0;
294
295                                 r = push(key, value, userdata);
296                                 if (r < 0)
297                                         goto fail;
298
299                                 n_key = 0;
300                                 value = NULL;
301                                 value_alloc = n_value = 0;
302                         } else if (c == '\\') {
303                                 state = VALUE_ESCAPE;
304                                 last_whitespace = (size_t) -1;
305                         } else {
306                                 if (!strchr(WHITESPACE, c))
307                                         last_whitespace = (size_t) -1;
308                                 else if (last_whitespace == (size_t) -1)
309                                         last_whitespace = n_value;
310
311                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
312                                         r = -ENOMEM;
313                                         goto fail;
314                                 }
315
316                                 value[n_value++] = c;
317                         }
318
319                         break;
320
321                 case VALUE_ESCAPE:
322                         state = VALUE;
323
324                         if (!strchr(newline, c)) {
325                                 /* Escaped newlines we eat up entirely */
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                         break;
334
335                 case SINGLE_QUOTE_VALUE:
336                         if (c == '\'')
337                                 state = PRE_VALUE;
338                         else if (c == '\\')
339                                 state = SINGLE_QUOTE_VALUE_ESCAPE;
340                         else {
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
349                         break;
350
351                 case SINGLE_QUOTE_VALUE_ESCAPE:
352                         state = SINGLE_QUOTE_VALUE;
353
354                         if (!strchr(newline, c)) {
355                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
356                                         r = -ENOMEM;
357                                         goto fail;
358                                 }
359
360                                 value[n_value++] = c;
361                         }
362                         break;
363
364                 case DOUBLE_QUOTE_VALUE:
365                         if (c == '\"')
366                                 state = PRE_VALUE;
367                         else if (c == '\\')
368                                 state = DOUBLE_QUOTE_VALUE_ESCAPE;
369                         else {
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
378                         break;
379
380                 case DOUBLE_QUOTE_VALUE_ESCAPE:
381                         state = DOUBLE_QUOTE_VALUE;
382
383                         if (!strchr(newline, c)) {
384                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
385                                         r = -ENOMEM;
386                                         goto fail;
387                                 }
388
389                                 value[n_value++] = c;
390                         }
391                         break;
392
393                 case COMMENT:
394                         if (c == '\\')
395                                 state = COMMENT_ESCAPE;
396                         else if (strchr(newline, c))
397                                 state = PRE_KEY;
398                         break;
399
400                 case COMMENT_ESCAPE:
401                         state = COMMENT;
402                         break;
403                 }
404         }
405
406         if (state == PRE_VALUE ||
407             state == VALUE ||
408             state == VALUE_ESCAPE ||
409             state == SINGLE_QUOTE_VALUE ||
410             state == SINGLE_QUOTE_VALUE_ESCAPE ||
411             state == DOUBLE_QUOTE_VALUE ||
412             state == DOUBLE_QUOTE_VALUE_ESCAPE) {
413
414                 key[n_key] = 0;
415
416                 if (value)
417                         value[n_value] = 0;
418
419                 /* strip trailing whitespace from key */
420                 while(strchr(WHITESPACE, key[--n_key]))
421                         key[n_key]=0;
422
423                 r = push(key, value, userdata);
424                 if (r < 0)
425                         goto fail;
426         }
427
428         return 0;
429
430 fail:
431         free(value);
432         return r;
433 }
434
435 static int parse_env_file_push(const char *key, char *value, void *userdata) {
436         const char *k;
437         va_list* ap = (va_list*) userdata;
438         va_list aq;
439
440         va_copy(aq, *ap);
441
442         while ((k = va_arg(aq, const char *))) {
443                 char **v;
444
445                 v = va_arg(aq, char **);
446
447                 if (streq(key, k)) {
448                         va_end(aq);
449                         free(*v);
450                         *v = value;
451                         return 1;
452                 }
453         }
454
455         va_end(aq);
456
457         free(value);
458         return 0;
459 }
460
461 int parse_env_file(
462                 const char *fname,
463                 const char *newline, ...) {
464
465         va_list ap;
466         int r;
467
468         if (!newline)
469                 newline = NEWLINE;
470
471         va_start(ap, newline);
472         r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap);
473         va_end(ap);
474
475         return r;
476 }
477
478 static int load_env_file_push(const char *key, char *value, void *userdata) {
479         char ***m = userdata;
480         char *p;
481         int r;
482
483         p = strjoin(key, "=", strempty(value), NULL);
484         if (!p)
485                 return -ENOMEM;
486
487         r = strv_push(m, p);
488         if (r < 0) {
489                 free(p);
490                 return r;
491         }
492
493         free(value);
494         return 0;
495 }
496
497 int load_env_file(const char *fname, const char *newline, char ***rl) {
498         char **m = NULL;
499         int r;
500
501         if (!newline)
502                 newline = NEWLINE;
503
504         r = parse_env_file_internal(fname, newline, load_env_file_push, &m);
505         if (r < 0) {
506                 strv_free(m);
507                 return r;
508         }
509
510         *rl = m;
511         return 0;
512 }
513
514 static void write_env_var(FILE *f, const char *v) {
515         const char *p;
516
517         p = strchr(v, '=');
518         if (!p) {
519                 /* Fallback */
520                 fputs(v, f);
521                 fputc('\n', f);
522                 return;
523         }
524
525         p++;
526         fwrite(v, 1, p-v, f);
527
528         if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\`$")) {
529                 fputc('\"', f);
530
531                 for (; *p; p++) {
532                         if (strchr("\'\"\\`$", *p))
533                                 fputc('\\', f);
534
535                         fputc(*p, f);
536                 }
537
538                 fputc('\"', f);
539         } else
540                 fputs(p, f);
541
542         fputc('\n', f);
543 }
544
545 int write_env_file(const char *fname, char **l) {
546         char **i;
547         char _cleanup_free_ *p = NULL;
548         FILE _cleanup_fclose_ *f = NULL;
549         int r;
550
551         r = fopen_temporary(fname, &f, &p);
552         if (r < 0)
553                 return r;
554
555         fchmod_umask(fileno(f), 0644);
556
557         errno = 0;
558         STRV_FOREACH(i, l)
559                 write_env_var(f, *i);
560
561         fflush(f);
562
563         if (ferror(f))
564                 r = errno ? -errno : -EIO;
565         else {
566                 if (rename(p, fname) < 0)
567                         r = -errno;
568                 else
569                         r = 0;
570         }
571
572         if (r < 0)
573                 unlink(p);
574
575         return r;
576 }