]> git.ipfire.org Git - thirdparty/kmod.git/blob - shared/util.c
2d44107afaef83906fb4a65597a8d4a9b62fccdc
[thirdparty/kmod.git] / shared / util.c
1 /*
2 * kmod - interface to kernel module operations
3 *
4 * Copyright (C) 2011-2013 ProFUSION embedded systems
5 * Copyright (C) 2012 Lucas De Marchi <lucas.de.marchi@gmail.com>
6 * Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but 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
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include <assert.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stddef.h>
27 #include <stdarg.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <ctype.h>
32
33 #include <shared/util.h>
34
35 #define USEC_PER_SEC 1000000ULL
36 #define NSEC_PER_USEC 1000ULL
37
38 /* string handling functions and memory allocations */
39 /* ************************************************************************ */
40
41 void *memdup(const void *p, size_t n)
42 {
43 void *r = malloc(n);
44
45 if (r == NULL)
46 return NULL;
47
48 return memcpy(r, p, n);
49 }
50
51 char *strchr_replace(char *s, int c, char r)
52 {
53 char *p;
54
55 for (p = s; *p != '\0'; p++) {
56 if (*p == c)
57 *p = r;
58 }
59
60 return s;
61 }
62
63 /* read-like and fread-like functions */
64 /* ************************************************************************ */
65 ssize_t read_str_safe(int fd, char *buf, size_t buflen)
66 {
67 size_t todo = buflen - 1;
68 size_t done = 0;
69
70 do {
71 ssize_t r = read(fd, buf + done, todo);
72
73 if (r == 0)
74 break;
75 else if (r > 0) {
76 todo -= r;
77 done += r;
78 } else {
79 if (errno == EAGAIN || errno == EWOULDBLOCK ||
80 errno == EINTR)
81 continue;
82 else
83 return -errno;
84 }
85 } while (todo > 0);
86
87 buf[done] = '\0';
88 return done;
89 }
90
91 ssize_t write_str_safe(int fd, const char *buf, size_t buflen)
92 {
93 size_t todo = buflen;
94 size_t done = 0;
95
96 do {
97 ssize_t r = write(fd, buf + done, todo);
98
99 if (r == 0)
100 break;
101 else if (r > 0) {
102 todo -= r;
103 done += r;
104 } else {
105 if (errno == EAGAIN || errno == EWOULDBLOCK ||
106 errno == EINTR)
107 continue;
108 else
109 return -errno;
110 }
111 } while (todo > 0);
112
113 return done;
114 }
115
116 int read_str_long(int fd, long *value, int base)
117 {
118 char buf[32], *end;
119 long v;
120 int err;
121
122 *value = 0;
123 err = read_str_safe(fd, buf, sizeof(buf));
124 if (err < 0)
125 return err;
126 errno = 0;
127 v = strtol(buf, &end, base);
128 if (end == buf || !isspace(*end))
129 return -EINVAL;
130
131 *value = v;
132 return 0;
133 }
134
135 int read_str_ulong(int fd, unsigned long *value, int base)
136 {
137 char buf[32], *end;
138 long v;
139 int err;
140
141 *value = 0;
142 err = read_str_safe(fd, buf, sizeof(buf));
143 if (err < 0)
144 return err;
145 errno = 0;
146 v = strtoul(buf, &end, base);
147 if (end == buf || !isspace(*end))
148 return -EINVAL;
149 *value = v;
150 return 0;
151 }
152
153 /*
154 * Read one logical line from a configuration file.
155 *
156 * Line endings may be escaped with backslashes, to form one logical line from
157 * several physical lines. No end of line character(s) are included in the
158 * result.
159 *
160 * If linenum is not NULL, it is incremented by the number of physical lines
161 * which have been read.
162 */
163 char *getline_wrapped(FILE *fp, unsigned int *linenum)
164 {
165 int size = 256;
166 int i = 0, n = 0;
167 _cleanup_free_ char *buf = malloc(size);
168
169 if (buf == NULL)
170 return NULL;
171
172 for(;;) {
173 int ch = getc_unlocked(fp);
174
175 switch(ch) {
176 case EOF:
177 if (i == 0)
178 return NULL;
179 /* else fall through */
180
181 case '\n':
182 n++;
183
184 {
185 char *ret;
186 if (i == size) {
187 ret = realloc(buf, size + 1);
188 if (!ret)
189 return NULL;
190 } else
191 ret = buf;
192 ret[i] = '\0';
193 buf = NULL;
194 if (linenum)
195 *linenum += n;
196 return ret;
197 }
198
199 case '\\':
200 ch = getc_unlocked(fp);
201
202 if (ch == '\n') {
203 n++;
204 continue;
205 }
206 /* else fall through */
207
208 default:
209 buf[i++] = ch;
210
211 if (i == size) {
212 char *tmp;
213 size *= 2;
214 tmp = realloc(buf, size);
215 if (!tmp)
216 return NULL;
217 buf = tmp;
218 }
219 }
220 }
221 }
222
223 /* path handling functions */
224 /* ************************************************************************ */
225
226 bool path_is_absolute(const char *p)
227 {
228 assert(p != NULL);
229
230 return p[0] == '/';
231 }
232
233 char *path_make_absolute_cwd(const char *p)
234 {
235 _cleanup_free_ char *cwd = NULL;
236 size_t plen, cwdlen;
237 char *r;
238
239 if (path_is_absolute(p))
240 return strdup(p);
241
242 cwd = get_current_dir_name();
243 if (!cwd)
244 return NULL;
245
246 plen = strlen(p);
247 cwdlen = strlen(cwd);
248
249 /* cwd + '/' + p + '\0' */
250 r = realloc(cwd, cwdlen + 1 + plen + 1);
251 if (r == NULL)
252 return NULL;
253
254 cwd = NULL;
255 r[cwdlen] = '/';
256 memcpy(&r[cwdlen + 1], p, plen + 1);
257
258 return r;
259 }
260
261 static inline int is_dir(const char *path)
262 {
263 struct stat st;
264
265 if (stat(path, &st) >= 0)
266 return S_ISDIR(st.st_mode);
267
268 return -errno;
269 }
270
271 int mkdir_p(const char *path, int len, mode_t mode)
272 {
273 char *start, *end;
274
275 start = strndupa(path, len);
276 end = start + len;
277
278 /*
279 * scan backwards, replacing '/' with '\0' while the component doesn't
280 * exist
281 */
282 for (;;) {
283 int r = is_dir(start);
284 if (r > 0) {
285 end += strlen(end);
286
287 if (end == start + len)
288 return 0;
289
290 /* end != start, since it would be caught on the first
291 * iteration */
292 *end = '/';
293 break;
294 } else if (r == 0)
295 return -ENOTDIR;
296
297 if (end == start)
298 break;
299
300 *end = '\0';
301
302 /* Find the next component, backwards, discarding extra '/'*/
303 while (end > start && *end != '/')
304 end--;
305
306 while (end > start && *(end - 1) == '/')
307 end--;
308 }
309
310 for (; end < start + len;) {
311 if (mkdir(start, mode) < 0 && errno != EEXIST)
312 return -errno;
313
314 end += strlen(end);
315 *end = '/';
316 }
317
318 return 0;
319 }
320
321 int mkdir_parents(const char *path, mode_t mode)
322 {
323 char *end = strrchr(path, '/');
324
325 /* no parent directories */
326 if (end == NULL)
327 return 0;
328
329 return mkdir_p(path, end - path, mode);
330 }
331
332 unsigned long long ts_usec(const struct timespec *ts)
333 {
334 return (unsigned long long) ts->tv_sec * USEC_PER_SEC +
335 (unsigned long long) ts->tv_nsec / NSEC_PER_USEC;
336 }
337
338 unsigned long long stat_mstamp(const struct stat *st)
339 {
340 #ifdef HAVE_STRUCT_STAT_ST_MTIM
341 return ts_usec(&st->st_mtim);
342 #else
343 return (unsigned long long) st->st_mtime;
344 #endif
345 }