chiark / gitweb /
4e6ff16045d68ff4e4b6c26d97c2ae50080eaf31
[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_one_line_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         if (fputs(line, f) < 0)
39                 return errno ? -errno : -EIO;
40
41         if (!endswith(line, "\n"))
42                 fputc('\n', f);
43
44         fflush(f);
45
46         if (ferror(f))
47                 return errno ? -errno : -EIO;
48
49         return 0;
50 }
51
52 int write_one_line_file_atomic(const char *fn, const char *line) {
53         _cleanup_fclose_ FILE *f = NULL;
54         _cleanup_free_ char *p = NULL;
55         int r;
56
57         assert(fn);
58         assert(line);
59
60         r = fopen_temporary(fn, &f, &p);
61         if (r < 0)
62                 return r;
63
64         fchmod_umask(fileno(f), 0644);
65
66         errno = 0;
67         if (fputs(line, f) < 0) {
68                 r = -errno;
69                 goto finish;
70         }
71
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 finish:
87         if (r < 0)
88                 unlink(p);
89
90         return r;
91 }
92
93 int read_one_line_file(const char *fn, char **line) {
94         _cleanup_fclose_ FILE *f = NULL;
95         char t[LINE_MAX], *c;
96
97         assert(fn);
98         assert(line);
99
100         f = fopen(fn, "re");
101         if (!f)
102                 return -errno;
103
104         if (!fgets(t, sizeof(t), f)) {
105
106                 if (ferror(f))
107                         return errno ? -errno : -EIO;
108
109                 t[0] = 0;
110         }
111
112         c = strdup(t);
113         if (!c)
114                 return -ENOMEM;
115         truncate_nl(c);
116
117         *line = c;
118         return 0;
119 }
120
121 int read_full_file(const char *fn, char **contents, size_t *size) {
122         _cleanup_fclose_ FILE *f = NULL;
123         size_t n, l;
124         _cleanup_free_ char *buf = NULL;
125         struct stat st;
126
127         assert(fn);
128         assert(contents);
129
130         f = fopen(fn, "re");
131         if (!f)
132                 return -errno;
133
134         if (fstat(fileno(f), &st) < 0)
135                 return -errno;
136
137         /* Safety check */
138         if (st.st_size > 4*1024*1024)
139                 return -E2BIG;
140
141         n = st.st_size > 0 ? st.st_size : LINE_MAX;
142         l = 0;
143
144         for (;;) {
145                 char *t;
146                 size_t k;
147
148                 t = realloc(buf, n+1);
149                 if (!t)
150                         return -ENOMEM;
151
152                 buf = t;
153                 k = fread(buf + l, 1, n - l, f);
154
155                 if (k <= 0) {
156                         if (ferror(f))
157                                 return -errno;
158
159                         break;
160                 }
161
162                 l += k;
163                 n *= 2;
164
165                 /* Safety check */
166                 if (n > 4*1024*1024)
167                         return -E2BIG;
168         }
169
170         buf[l] = 0;
171         *contents = buf;
172         buf = NULL;
173
174         if (size)
175                 *size = l;
176
177         return 0;
178 }
179
180 int parse_env_file(
181                 const char *fname,
182                 const char *separator, ...) {
183
184         int r = 0;
185         char *contents = NULL, *p;
186
187         assert(fname);
188         assert(separator);
189
190         if ((r = read_full_file(fname, &contents, NULL)) < 0)
191                 return r;
192
193         p = contents;
194         for (;;) {
195                 const char *key = NULL;
196
197                 p += strspn(p, separator);
198                 p += strspn(p, WHITESPACE);
199
200                 if (!*p)
201                         break;
202
203                 if (!strchr(COMMENTS, *p)) {
204                         va_list ap;
205                         char **value;
206
207                         va_start(ap, separator);
208                         while ((key = va_arg(ap, char *))) {
209                                 size_t n;
210                                 char *v;
211
212                                 value = va_arg(ap, char **);
213
214                                 n = strlen(key);
215                                 if (!strneq(p, key, n) ||
216                                     p[n] != '=')
217                                         continue;
218
219                                 p += n + 1;
220                                 n = strcspn(p, separator);
221
222                                 if (n >= 2 &&
223                                     strchr(QUOTES, p[0]) &&
224                                     p[n-1] == p[0])
225                                         v = strndup(p+1, n-2);
226                                 else
227                                         v = strndup(p, n);
228
229                                 if (!v) {
230                                         r = -ENOMEM;
231                                         va_end(ap);
232                                         goto fail;
233                                 }
234
235                                 if (v[0] == '\0') {
236                                         /* return empty value strings as NULL */
237                                         free(v);
238                                         v = NULL;
239                                 }
240
241                                 free(*value);
242                                 *value = v;
243
244                                 p += n;
245
246                                 r ++;
247                                 break;
248                         }
249                         va_end(ap);
250                 }
251
252                 if (!key)
253                         p += strcspn(p, separator);
254         }
255
256 fail:
257         free(contents);
258         return r;
259 }
260
261 int load_env_file(const char *fname, char ***rl) {
262
263         _cleanup_fclose_ FILE *f;
264         _cleanup_strv_free_ char **m = NULL;
265         _cleanup_free_ char *c = NULL;
266
267         assert(fname);
268         assert(rl);
269
270         /* This reads an environment file, but will not complain about
271          * any invalid assignments, that needs to be done by the
272          * caller */
273
274         f = fopen(fname, "re");
275         if (!f)
276                 return -errno;
277
278         while (!feof(f)) {
279                 char l[LINE_MAX], *p, *cs, *b;
280
281                 if (!fgets(l, sizeof(l), f)) {
282                         if (ferror(f))
283                                 return -errno;
284
285                         /* The previous line was a continuation line?
286                          * Let's process it now, before we leave the
287                          * loop */
288                         if (c)
289                                 goto process;
290
291                         break;
292                 }
293
294                 /* Is this a continuation line? If so, just append
295                  * this to c, and go to next line right-away */
296                 cs = endswith(l, "\\\n");
297                 if (cs) {
298                         *cs = '\0';
299                         b = strappend(c, l);
300                         if (!b)
301                                 return -ENOMEM;
302
303                         free(c);
304                         c = b;
305                         continue;
306                 }
307
308                 /* If the previous line was a continuation line,
309                  * append the current line to it */
310                 if (c) {
311                         b = strappend(c, l);
312                         if (!b)
313                                 return -ENOMEM;
314
315                         free(c);
316                         c = b;
317                 }
318
319         process:
320                 p = strstrip(c ? c : l);
321
322                 if (*p && !strchr(COMMENTS, *p)) {
323                         _cleanup_free_ char *u;
324                         int k;
325
326                         u = normalize_env_assignment(p);
327                         if (!u)
328                                 return -ENOMEM;
329
330                         k = strv_extend(&m, u);
331                         if (k < 0)
332                                 return -ENOMEM;
333                 }
334
335                 free(c);
336                 c = NULL;
337         }
338
339         *rl = m;
340         m = NULL;
341
342         return 0;
343 }
344
345 int write_env_file(const char *fname, char **l) {
346         char **i, *p;
347         FILE *f;
348         int r;
349
350         r = fopen_temporary(fname, &f, &p);
351         if (r < 0)
352                 return r;
353
354         fchmod_umask(fileno(f), 0644);
355
356         errno = 0;
357         STRV_FOREACH(i, l) {
358                 fputs(*i, f);
359                 fputc('\n', f);
360         }
361
362         fflush(f);
363
364         if (ferror(f)) {
365                 if (errno != 0)
366                         r = -errno;
367                 else
368                         r = -EIO;
369         } else {
370                 if (rename(p, fname) < 0)
371                         r = -errno;
372                 else
373                         r = 0;
374         }
375
376         if (r < 0)
377                 unlink(p);
378
379         fclose(f);
380         free(p);
381
382         return r;
383 }