chiark / gitweb /
catalog: open up catalog internals
[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         r = read_full_file(fname, &contents, NULL);
191         if (r < 0)
192                 return r;
193
194         p = contents;
195         for (;;) {
196                 const char *key = NULL;
197
198                 p += strspn(p, separator);
199                 p += strspn(p, WHITESPACE);
200
201                 if (!*p)
202                         break;
203
204                 if (!strchr(COMMENTS, *p)) {
205                         va_list ap;
206                         char **value;
207
208                         va_start(ap, separator);
209                         while ((key = va_arg(ap, char *))) {
210                                 size_t n;
211                                 char *v;
212
213                                 value = va_arg(ap, char **);
214
215                                 n = strlen(key);
216                                 if (!strneq(p, key, n) ||
217                                     p[n] != '=')
218                                         continue;
219
220                                 p += n + 1;
221                                 n = strcspn(p, separator);
222
223                                 if (n >= 2 &&
224                                     strchr(QUOTES, p[0]) &&
225                                     p[n-1] == p[0])
226                                         v = strndup(p+1, n-2);
227                                 else
228                                         v = strndup(p, n);
229
230                                 if (!v) {
231                                         r = -ENOMEM;
232                                         va_end(ap);
233                                         goto fail;
234                                 }
235
236                                 if (v[0] == '\0') {
237                                         /* return empty value strings as NULL */
238                                         free(v);
239                                         v = NULL;
240                                 }
241
242                                 free(*value);
243                                 *value = v;
244
245                                 p += n;
246
247                                 r ++;
248                                 break;
249                         }
250                         va_end(ap);
251                 }
252
253                 if (!key)
254                         p += strcspn(p, separator);
255         }
256
257 fail:
258         free(contents);
259         return r;
260 }
261
262 int load_env_file(const char *fname, char ***rl) {
263
264         _cleanup_fclose_ FILE *f;
265         _cleanup_strv_free_ char **m = NULL;
266         _cleanup_free_ char *c = NULL;
267
268         assert(fname);
269         assert(rl);
270
271         /* This reads an environment file, but will not complain about
272          * any invalid assignments, that needs to be done by the
273          * caller */
274
275         f = fopen(fname, "re");
276         if (!f)
277                 return -errno;
278
279         while (!feof(f)) {
280                 char l[LINE_MAX], *p, *cs, *b;
281
282                 if (!fgets(l, sizeof(l), f)) {
283                         if (ferror(f))
284                                 return -errno;
285
286                         /* The previous line was a continuation line?
287                          * Let's process it now, before we leave the
288                          * loop */
289                         if (c)
290                                 goto process;
291
292                         break;
293                 }
294
295                 /* Is this a continuation line? If so, just append
296                  * this to c, and go to next line right-away */
297                 cs = endswith(l, "\\\n");
298                 if (cs) {
299                         *cs = '\0';
300                         b = strappend(c, l);
301                         if (!b)
302                                 return -ENOMEM;
303
304                         free(c);
305                         c = b;
306                         continue;
307                 }
308
309                 /* If the previous line was a continuation line,
310                  * append the current line to it */
311                 if (c) {
312                         b = strappend(c, l);
313                         if (!b)
314                                 return -ENOMEM;
315
316                         free(c);
317                         c = b;
318                 }
319
320         process:
321                 p = strstrip(c ? c : l);
322
323                 if (*p && !strchr(COMMENTS, *p)) {
324                         _cleanup_free_ char *u;
325                         int k;
326
327                         u = normalize_env_assignment(p);
328                         if (!u)
329                                 return -ENOMEM;
330
331                         k = strv_extend(&m, u);
332                         if (k < 0)
333                                 return -ENOMEM;
334                 }
335
336                 free(c);
337                 c = NULL;
338         }
339
340         *rl = m;
341         m = NULL;
342
343         return 0;
344 }
345
346 int write_env_file(const char *fname, char **l) {
347         char **i;
348         char _cleanup_free_ *p = NULL;
349         FILE _cleanup_fclose_ *f = NULL;
350         int r;
351
352         r = fopen_temporary(fname, &f, &p);
353         if (r < 0)
354                 return r;
355
356         fchmod_umask(fileno(f), 0644);
357
358         errno = 0;
359         STRV_FOREACH(i, l) {
360                 fputs(*i, f);
361                 fputc('\n', f);
362         }
363
364         fflush(f);
365
366         if (ferror(f)) {
367                 if (errno != 0)
368                         r = -errno;
369                 else
370                         r = -EIO;
371         } else {
372                 if (rename(p, fname) < 0)
373                         r = -errno;
374                 else
375                         r = 0;
376         }
377
378         if (r < 0)
379                 unlink(p);
380
381         return r;
382 }