]> git.ipfire.org Git - thirdparty/kmod.git/blame - shared/util.c
make: install/uninstall tools symlinks to kmod
[thirdparty/kmod.git] / shared / util.c
CommitLineData
96573a02
LDM
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
dea2dfee 19 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
96573a02
LDM
20 */
21
22#include <assert.h>
c2e4286b
LDM
23#include <ctype.h>
24#include <errno.h>
25#include <stdarg.h>
26#include <stddef.h>
96573a02
LDM
27#include <stdio.h>
28#include <stdlib.h>
96573a02 29#include <string.h>
c2e4286b 30#include <unistd.h>
96573a02 31
efb5bfac 32#include <shared/missing.h>
96573a02
LDM
33#include <shared/util.h>
34
35#define USEC_PER_SEC 1000000ULL
36#define NSEC_PER_USEC 1000ULL
37
f4e8c162
LDM
38static const struct kmod_ext {
39 const char *ext;
40 size_t len;
41} kmod_exts[] = {
42 {KMOD_EXTENSION_UNCOMPRESSED, sizeof(KMOD_EXTENSION_UNCOMPRESSED) - 1},
43#ifdef ENABLE_ZLIB
44 {".ko.gz", sizeof(".ko.gz") - 1},
45#endif
46#ifdef ENABLE_XZ
47 {".ko.xz", sizeof(".ko.xz") - 1},
3821e197
TM
48#endif
49#ifdef ENABLE_ZSTD
50 {".ko.zst", sizeof(".ko.zst") - 1},
f4e8c162
LDM
51#endif
52 { }
53};
54
96573a02
LDM
55/* string handling functions and memory allocations */
56/* ************************************************************************ */
57
58void *memdup(const void *p, size_t n)
59{
60 void *r = malloc(n);
61
62 if (r == NULL)
63 return NULL;
64
65 return memcpy(r, p, n);
66}
67
9a437531 68char *strchr_replace(char *s, char c, char r)
96573a02
LDM
69{
70 char *p;
71
72 for (p = s; *p != '\0'; p++) {
73 if (*p == c)
74 *p = r;
75 }
76
77 return s;
78}
79
2b0104fe
LDM
80/* module-related functions */
81/* ************************************************************************ */
82int alias_normalize(const char *alias, char buf[static PATH_MAX], size_t *len)
83{
84 size_t i;
85
86 for (i = 0; i < PATH_MAX - 1; i++) {
87 const char c = alias[i];
88 switch (c) {
89 case '-':
90 buf[i] = '_';
91 break;
92 case ']':
93 return -EINVAL;
94 case '[':
95 while (alias[i] != ']' && alias[i] != '\0') {
96 buf[i] = alias[i];
97 i++;
98 }
99
100 if (alias[i] != ']')
101 return -EINVAL;
102
103 buf[i] = alias[i];
104 break;
105 case '\0':
106 goto finish;
107 default:
108 buf[i] = c;
109 }
110 }
111
112finish:
113 buf[i] = '\0';
114 if (len)
115 *len = i;
116
117 return 0;
118}
119
52c9c990
LDM
120/*
121 * Replace dashes with underscores.
122 * Dashes inside character range patterns (e.g. [0-9]) are left unchanged.
123 *
124 * For convenience, it returns error if @s is NULL
125 */
126int underscores(char *s)
127{
128 unsigned int i;
129
130 if (!s)
131 return -EINVAL;
132
133 for (i = 0; s[i]; i++) {
134 switch (s[i]) {
135 case '-':
136 s[i] = '_';
137 break;
138 case ']':
139 return -EINVAL;
140 case '[':
141 i += strcspn(&s[i], "]");
142 if (!s[i])
143 return -EINVAL;
144 break;
145 }
146 }
147
148 return 0;
149}
150
f4e8c162
LDM
151char *modname_normalize(const char *modname, char buf[static PATH_MAX], size_t *len)
152{
153 size_t s;
154
155 for (s = 0; s < PATH_MAX - 1; s++) {
156 const char c = modname[s];
157 if (c == '-')
158 buf[s] = '_';
159 else if (c == '\0' || c == '.')
160 break;
161 else
162 buf[s] = c;
163 }
164
165 buf[s] = '\0';
166
167 if (len)
168 *len = s;
169
170 return buf;
171}
172
173char *path_to_modname(const char *path, char buf[static PATH_MAX], size_t *len)
174{
175 char *modname;
176
177 modname = basename(path);
178 if (modname == NULL || modname[0] == '\0')
179 return NULL;
180
181 return modname_normalize(modname, buf, len);
182}
183
184bool path_ends_with_kmod_ext(const char *path, size_t len)
185{
186 const struct kmod_ext *eitr;
187
188 for (eitr = kmod_exts; eitr->ext != NULL; eitr++) {
189 if (len <= eitr->len)
190 continue;
191 if (streq(path + len - eitr->len, eitr->ext))
192 return true;
193 }
194
195 return false;
196}
197
96573a02
LDM
198/* read-like and fread-like functions */
199/* ************************************************************************ */
200ssize_t read_str_safe(int fd, char *buf, size_t buflen)
201{
202 size_t todo = buflen - 1;
203 size_t done = 0;
204
ecab65b3
TP
205 assert_cc(EAGAIN == EWOULDBLOCK);
206
96573a02
LDM
207 do {
208 ssize_t r = read(fd, buf + done, todo);
209
210 if (r == 0)
211 break;
212 else if (r > 0) {
213 todo -= r;
214 done += r;
215 } else {
a6ede6c7 216 if (errno == EAGAIN || errno == EINTR)
96573a02
LDM
217 continue;
218 else
219 return -errno;
220 }
221 } while (todo > 0);
222
223 buf[done] = '\0';
224 return done;
225}
226
227ssize_t write_str_safe(int fd, const char *buf, size_t buflen)
228{
229 size_t todo = buflen;
230 size_t done = 0;
231
ecab65b3
TP
232 assert_cc(EAGAIN == EWOULDBLOCK);
233
96573a02
LDM
234 do {
235 ssize_t r = write(fd, buf + done, todo);
236
237 if (r == 0)
238 break;
239 else if (r > 0) {
240 todo -= r;
241 done += r;
242 } else {
a6ede6c7 243 if (errno == EAGAIN || errno == EINTR)
96573a02
LDM
244 continue;
245 else
246 return -errno;
247 }
248 } while (todo > 0);
249
250 return done;
251}
252
253int read_str_long(int fd, long *value, int base)
254{
255 char buf[32], *end;
256 long v;
257 int err;
258
259 *value = 0;
260 err = read_str_safe(fd, buf, sizeof(buf));
261 if (err < 0)
262 return err;
263 errno = 0;
264 v = strtol(buf, &end, base);
265 if (end == buf || !isspace(*end))
266 return -EINVAL;
267
268 *value = v;
269 return 0;
270}
271
272int read_str_ulong(int fd, unsigned long *value, int base)
273{
274 char buf[32], *end;
275 long v;
276 int err;
277
278 *value = 0;
279 err = read_str_safe(fd, buf, sizeof(buf));
280 if (err < 0)
281 return err;
282 errno = 0;
283 v = strtoul(buf, &end, base);
284 if (end == buf || !isspace(*end))
285 return -EINVAL;
286 *value = v;
287 return 0;
288}
289
290/*
291 * Read one logical line from a configuration file.
292 *
293 * Line endings may be escaped with backslashes, to form one logical line from
294 * several physical lines. No end of line character(s) are included in the
295 * result.
296 *
297 * If linenum is not NULL, it is incremented by the number of physical lines
298 * which have been read.
299 */
aafd3835 300char *freadline_wrapped(FILE *fp, unsigned int *linenum)
96573a02
LDM
301{
302 int size = 256;
303 int i = 0, n = 0;
304 _cleanup_free_ char *buf = malloc(size);
305
306 if (buf == NULL)
307 return NULL;
308
309 for(;;) {
310 int ch = getc_unlocked(fp);
311
312 switch(ch) {
313 case EOF:
314 if (i == 0)
315 return NULL;
316 /* else fall through */
317
318 case '\n':
319 n++;
320
321 {
88f6ffe4 322 char *ret = buf;
96573a02
LDM
323 ret[i] = '\0';
324 buf = NULL;
325 if (linenum)
326 *linenum += n;
327 return ret;
328 }
329
330 case '\\':
331 ch = getc_unlocked(fp);
332
333 if (ch == '\n') {
334 n++;
335 continue;
336 }
337 /* else fall through */
338
339 default:
340 buf[i++] = ch;
341
342 if (i == size) {
343 char *tmp;
344 size *= 2;
345 tmp = realloc(buf, size);
346 if (!tmp)
347 return NULL;
348 buf = tmp;
349 }
350 }
351 }
352}
353
354/* path handling functions */
355/* ************************************************************************ */
356
06e6f167 357static bool path_is_absolute(const char *p)
96573a02
LDM
358{
359 assert(p != NULL);
360
361 return p[0] == '/';
362}
363
364char *path_make_absolute_cwd(const char *p)
365{
366 _cleanup_free_ char *cwd = NULL;
367 size_t plen, cwdlen;
368 char *r;
369
370 if (path_is_absolute(p))
371 return strdup(p);
372
373 cwd = get_current_dir_name();
374 if (!cwd)
375 return NULL;
376
377 plen = strlen(p);
378 cwdlen = strlen(cwd);
379
380 /* cwd + '/' + p + '\0' */
381 r = realloc(cwd, cwdlen + 1 + plen + 1);
382 if (r == NULL)
383 return NULL;
384
385 cwd = NULL;
386 r[cwdlen] = '/';
387 memcpy(&r[cwdlen + 1], p, plen + 1);
388
389 return r;
390}
391
392static inline int is_dir(const char *path)
393{
394 struct stat st;
395
396 if (stat(path, &st) >= 0)
397 return S_ISDIR(st.st_mode);
398
399 return -errno;
400}
401
402int mkdir_p(const char *path, int len, mode_t mode)
403{
404 char *start, *end;
405
406 start = strndupa(path, len);
407 end = start + len;
408
409 /*
410 * scan backwards, replacing '/' with '\0' while the component doesn't
411 * exist
412 */
413 for (;;) {
414 int r = is_dir(start);
415 if (r > 0) {
416 end += strlen(end);
417
418 if (end == start + len)
419 return 0;
420
421 /* end != start, since it would be caught on the first
422 * iteration */
423 *end = '/';
424 break;
425 } else if (r == 0)
426 return -ENOTDIR;
427
428 if (end == start)
429 break;
430
431 *end = '\0';
432
433 /* Find the next component, backwards, discarding extra '/'*/
434 while (end > start && *end != '/')
435 end--;
436
437 while (end > start && *(end - 1) == '/')
438 end--;
439 }
440
441 for (; end < start + len;) {
442 if (mkdir(start, mode) < 0 && errno != EEXIST)
443 return -errno;
444
445 end += strlen(end);
446 *end = '/';
447 }
448
449 return 0;
450}
451
452int mkdir_parents(const char *path, mode_t mode)
453{
454 char *end = strrchr(path, '/');
455
456 /* no parent directories */
457 if (end == NULL)
458 return 0;
459
460 return mkdir_p(path, end - path, mode);
461}
462
06e6f167 463static unsigned long long ts_usec(const struct timespec *ts)
96573a02
LDM
464{
465 return (unsigned long long) ts->tv_sec * USEC_PER_SEC +
466 (unsigned long long) ts->tv_nsec / NSEC_PER_USEC;
467}
468
06e6f167 469static unsigned long long ts_msec(const struct timespec *ts)
ba105faf
LDM
470{
471 return (unsigned long long) ts->tv_sec * MSEC_PER_SEC +
472 (unsigned long long) ts->tv_nsec / NSEC_PER_MSEC;
473}
474
8ab15ece
LDM
475static struct timespec msec_ts(unsigned long long msec)
476{
477 struct timespec ts = {
478 .tv_sec = msec / MSEC_PER_SEC,
479 .tv_nsec = (msec % MSEC_PER_SEC) * NSEC_PER_MSEC,
480 };
481
482 return ts;
483}
484
485int sleep_until_msec(unsigned long long msec)
486{
487 struct timespec ts = msec_ts(msec);
488
489 if (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL) < 0 &&
490 errno != EINTR)
491 return -errno;
492
493 return 0;
494}
495
496/*
497 * Exponential retry backoff with tail
498 */
499unsigned long long get_backoff_delta_msec(unsigned long long t0,
500 unsigned long long tend,
501 unsigned long long *delta)
502{
503 unsigned long long t;
504
505 t = now_msec();
506
507 if (!*delta)
508 *delta = 1;
509 else
510 *delta <<= 1;
511
512 while (t + *delta > tend)
513 *delta >>= 1;
514
515 if (!*delta && tend > t)
516 *delta = tend - t;
517
518 return t + *delta;
519}
520
5622f1da
LDM
521unsigned long long now_usec(void)
522{
523 struct timespec ts;
524
525 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
526 return 0;
527
528 return ts_usec(&ts);
529}
530
ba105faf
LDM
531unsigned long long now_msec(void)
532{
533 struct timespec ts;
534
535 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
536 return 0;
537
538 return ts_msec(&ts);
539}
540
96573a02
LDM
541unsigned long long stat_mstamp(const struct stat *st)
542{
543#ifdef HAVE_STRUCT_STAT_ST_MTIM
544 return ts_usec(&st->st_mtim);
545#else
546 return (unsigned long long) st->st_mtime;
547#endif
548}