multipath-tools: fix more gcc 9 -Wstringop-truncation warnings
[multipath-tools/.git] / libmultipath / util.c
1 #include <assert.h>
2 #include <ctype.h>
3 #include <limits.h>
4 #include <pthread.h>
5 #include <string.h>
6 #include <sys/stat.h>
7 #include <sys/sysmacros.h>
8 #include <sys/types.h>
9 #include <sys/utsname.h>
10 #include <sys/time.h>
11 #include <sys/resource.h>
12 #include <dirent.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include <libudev.h>
16 #include <mpath_persist.h>
17
18 #include "util.h"
19 #include "debug.h"
20 #include "memory.h"
21 #include "checkers.h"
22 #include "vector.h"
23 #include "structs.h"
24 #include "log.h"
25
26 size_t
27 strchop(char *str)
28 {
29         int i;
30
31         for (i=strlen(str)-1; i >=0 && isspace(str[i]); --i) ;
32         str[++i] = '\0';
33         return strlen(str);
34 }
35
36 int
37 basenamecpy (const char *src, char *dst, size_t size)
38 {
39         const char *p, *e;
40
41         if (!src || !dst || !strlen(src))
42                 return 0;
43
44         p = basename(src);
45
46         for (e = p + strlen(p) - 1; e >= p && isspace(*e); --e) ;
47         if (e < p || e - p > size - 2)
48                 return 0;
49
50         strlcpy(dst, p, e - p + 2);
51         return strlen(dst);
52 }
53
54 int
55 filepresent (char * run) {
56         struct stat buf;
57
58         if(!stat(run, &buf))
59                 return 1;
60         return 0;
61 }
62
63 char *get_next_string(char **temp, char *split_char)
64 {
65         char *token = NULL;
66         token = strsep(temp, split_char);
67         while (token != NULL && !strcmp(token, ""))
68                 token = strsep(temp, split_char);
69         return token;
70 }
71
72 int
73 get_word (char * sentence, char ** word)
74 {
75         char * p;
76         int len;
77         int skip = 0;
78
79         if (word)
80                 *word = NULL;
81
82         while (*sentence ==  ' ') {
83                 sentence++;
84                 skip++;
85         }
86         if (*sentence == '\0')
87                 return 0;
88
89         p = sentence;
90
91         while (*p !=  ' ' && *p != '\0')
92                 p++;
93
94         len = (int) (p - sentence);
95
96         if (!word)
97                 return skip + len;
98
99         *word = MALLOC(len + 1);
100
101         if (!*word) {
102                 condlog(0, "get_word : oom");
103                 return 0;
104         }
105         strncpy(*word, sentence, len);
106         strchop(*word);
107         condlog(5, "*word = %s, len = %i", *word, len);
108
109         if (*p == '\0')
110                 return 0;
111
112         return skip + len;
113 }
114
115 size_t strlcpy(char *dst, const char *src, size_t size)
116 {
117         size_t bytes = 0;
118         char *q = dst;
119         const char *p = src;
120         char ch;
121
122         while ((ch = *p++)) {
123                 if (bytes+1 < size)
124                         *q++ = ch;
125                 bytes++;
126         }
127
128         /* If size == 0 there is no space for a final null... */
129         if (size)
130                 *q = '\0';
131         return bytes;
132 }
133
134 size_t strlcat(char *dst, const char *src, size_t size)
135 {
136         size_t bytes = 0;
137         char *q = dst;
138         const char *p = src;
139         char ch;
140
141         while (bytes < size && *q) {
142                 q++;
143                 bytes++;
144         }
145         if (bytes == size)
146                 return (bytes + strlen(src));
147
148         while ((ch = *p++)) {
149                 if (bytes+1 < size)
150                 *q++ = ch;
151                 bytes++;
152         }
153
154         *q = '\0';
155         return bytes;
156 }
157
158 int devt2devname(char *devname, int devname_len, char *devt)
159 {
160         FILE *fd;
161         unsigned int tmpmaj, tmpmin, major, minor;
162         char dev[FILE_NAME_SIZE];
163         char block_path[PATH_SIZE];
164         struct stat statbuf;
165
166         memset(block_path, 0, sizeof(block_path));
167         memset(dev, 0, sizeof(dev));
168         if (sscanf(devt, "%u:%u", &major, &minor) != 2) {
169                 condlog(0, "Invalid device number %s", devt);
170                 return 1;
171         }
172
173         if (devname_len > FILE_NAME_SIZE)
174                 devname_len = FILE_NAME_SIZE;
175
176         if (stat("/sys/dev/block", &statbuf) == 0) {
177                 /* Newer kernels have /sys/dev/block */
178                 sprintf(block_path,"/sys/dev/block/%u:%u", major, minor);
179                 dev[FILE_NAME_SIZE - 1] = '\0';
180                 if (lstat(block_path, &statbuf) == 0) {
181                         if (S_ISLNK(statbuf.st_mode) &&
182                             readlink(block_path, dev, FILE_NAME_SIZE-1) > 0) {
183                                 char *p = strrchr(dev, '/');
184
185                                 if (!p) {
186                                         condlog(0, "No sysfs entry for %s",
187                                                 block_path);
188                                         return 1;
189                                 }
190                                 p++;
191                                 strlcpy(devname, p, devname_len);
192                                 return 0;
193                         }
194                 }
195                 condlog(4, "%s is invalid", block_path);
196                 return 1;
197         }
198         memset(block_path, 0, sizeof(block_path));
199
200         if (!(fd = fopen("/proc/partitions", "r"))) {
201                 condlog(0, "Cannot open /proc/partitions");
202                 return 1;
203         }
204
205         while (!feof(fd)) {
206                 int r = fscanf(fd,"%u %u %*d %s",&tmpmaj, &tmpmin, dev);
207                 if (!r) {
208                         r = fscanf(fd,"%*s\n");
209                         continue;
210                 }
211                 if (r != 3)
212                         continue;
213
214                 if ((major == tmpmaj) && (minor == tmpmin)) {
215                         if (snprintf(block_path, sizeof(block_path),
216                                      "/sys/block/%s", dev) >= sizeof(block_path)) {
217                                 condlog(0, "device name %s is too long", dev);
218                                 fclose(fd);
219                                 return 1;
220                         }
221                         break;
222                 }
223         }
224         fclose(fd);
225
226         if (strncmp(block_path,"/sys/block", 10)) {
227                 condlog(3, "No device found for %u:%u", major, minor);
228                 return 1;
229         }
230
231         if (stat(block_path, &statbuf) < 0) {
232                 condlog(0, "No sysfs entry for %s", block_path);
233                 return 1;
234         }
235
236         if (S_ISDIR(statbuf.st_mode) == 0) {
237                 condlog(0, "sysfs entry %s is not a directory", block_path);
238                 return 1;
239         }
240         basenamecpy((const char *)block_path, devname, devname_len);
241         return 0;
242 }
243
244 /* This function returns a pointer inside of the supplied pathname string.
245  * If is_path_device is true, it may also modify the supplied string */
246 char *convert_dev(char *name, int is_path_device)
247 {
248         char *ptr;
249
250         if (!name)
251                 return NULL;
252         if (is_path_device) {
253                 ptr = strstr(name, "cciss/");
254                 if (ptr) {
255                         ptr += 5;
256                         *ptr = '!';
257                 }
258         }
259         if (!strncmp(name, "/dev/", 5) && strlen(name) > 5)
260                 ptr = name + 5;
261         else
262                 ptr = name;
263         return ptr;
264 }
265
266 dev_t parse_devt(const char *dev_t)
267 {
268         int maj, min;
269
270         if (sscanf(dev_t,"%d:%d", &maj, &min) != 2)
271                 return 0;
272
273         return makedev(maj, min);
274 }
275
276 char *parse_uid_attribute_by_attrs(char *uid_attrs, char *path_dev)
277 {
278         char *uid_attribute;
279         char *uid_attr_record;
280         char *dev;
281         char *attr;
282         char *tmp;
283         int  count;
284
285         if(!uid_attrs || !path_dev)
286                 return NULL;
287
288         count = get_word(uid_attrs, &uid_attr_record);
289         while (uid_attr_record) {
290                 tmp = strrchr(uid_attr_record, ':');
291                 if (!tmp) {
292                         free(uid_attr_record);
293                         if (!count)
294                                 break;
295                         uid_attrs += count;
296                         count = get_word(uid_attrs, &uid_attr_record);
297                         continue;
298                 }
299                 dev = uid_attr_record;
300                 attr = tmp + 1;
301                 *tmp = '\0';
302
303                 if(!strncmp(path_dev, dev, strlen(dev))) {
304                         uid_attribute = STRDUP(attr);
305                         free(uid_attr_record);
306                         return uid_attribute;
307                 }
308
309                 free(uid_attr_record);
310                 if (!count)
311                         break;
312                 uid_attrs += count;
313                 count = get_word(uid_attrs, &uid_attr_record);
314         }
315         return NULL;
316 }
317
318 void
319 setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached)
320 {
321         int ret;
322
323         ret = pthread_attr_init(attr);
324         assert(ret == 0);
325         if (stacksize < PTHREAD_STACK_MIN)
326                 stacksize = PTHREAD_STACK_MIN;
327         ret = pthread_attr_setstacksize(attr, stacksize);
328         assert(ret == 0);
329         if (detached) {
330                 ret = pthread_attr_setdetachstate(attr,
331                                                   PTHREAD_CREATE_DETACHED);
332                 assert(ret == 0);
333         }
334 }
335
336 int systemd_service_enabled_in(const char *dev, const char *prefix)
337 {
338         char path[PATH_SIZE], file[PATH_MAX], service[PATH_SIZE];
339         DIR *dirfd;
340         struct dirent *d;
341         int found = 0;
342
343         snprintf(service, PATH_SIZE, "multipathd.service");
344         snprintf(path, PATH_SIZE, "%s/systemd/system", prefix);
345         condlog(3, "%s: checking for %s in %s", dev, service, path);
346
347         dirfd = opendir(path);
348         if (dirfd == NULL)
349                 return 0;
350
351         while ((d = readdir(dirfd)) != NULL) {
352                 char *p;
353                 struct stat stbuf;
354
355                 if ((strcmp(d->d_name,".") == 0) ||
356                     (strcmp(d->d_name,"..") == 0))
357                         continue;
358
359                 if (strlen(d->d_name) < 6)
360                         continue;
361
362                 p = d->d_name + strlen(d->d_name) - 6;
363                 if (strcmp(p, ".wants"))
364                         continue;
365                 snprintf(file, sizeof(file), "%s/%s/%s",
366                          path, d->d_name, service);
367                 if (stat(file, &stbuf) == 0) {
368                         condlog(3, "%s: found %s", dev, file);
369                         found++;
370                         break;
371                 }
372         }
373         closedir(dirfd);
374
375         return found;
376 }
377
378 int systemd_service_enabled(const char *dev)
379 {
380         int found = 0;
381
382         found = systemd_service_enabled_in(dev, "/etc");
383         if (!found)
384                 found = systemd_service_enabled_in(dev, "/usr/lib");
385         if (!found)
386                 found = systemd_service_enabled_in(dev, "/lib");
387         if (!found)
388                 found = systemd_service_enabled_in(dev, "/run");
389         return found;
390 }
391
392 static int _linux_version_code;
393 static pthread_once_t _lvc_initialized = PTHREAD_ONCE_INIT;
394
395 /* Returns current kernel version encoded as major*65536 + minor*256 + patch,
396  * so, for example,  to check if the kernel is greater than 2.2.11:
397  *
398  *     if (get_linux_version_code() > KERNEL_VERSION(2,2,11)) { <stuff> }
399  *
400  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
401  * Code copied from busybox (GPLv2 or later)
402  */
403 static void
404 _set_linux_version_code(void)
405 {
406         struct utsname name;
407         char *t;
408         int i, r;
409
410         uname(&name); /* never fails */
411         t = name.release;
412         r = 0;
413         for (i = 0; i < 3; i++) {
414                 t = strtok(t, ".");
415                 r = r * 256 + (t ? atoi(t) : 0);
416                 t = NULL;
417         }
418         _linux_version_code = r;
419 }
420
421 int get_linux_version_code(void)
422 {
423         pthread_once(&_lvc_initialized, _set_linux_version_code);
424         return _linux_version_code;
425 }
426
427 int parse_prkey(char *ptr, uint64_t *prkey)
428 {
429         if (!ptr)
430                 return 1;
431         if (*ptr == '0')
432                 ptr++;
433         if (*ptr == 'x' || *ptr == 'X')
434                 ptr++;
435         if (*ptr == '\0' || strlen(ptr) > 16)
436                 return 1;
437         if (strlen(ptr) != strspn(ptr, "0123456789aAbBcCdDeEfF"))
438                 return 1;
439         if (sscanf(ptr, "%" SCNx64 "", prkey) != 1)
440                 return 1;
441         return 0;
442 }
443
444 int parse_prkey_flags(char *ptr, uint64_t *prkey, uint8_t *flags)
445 {
446         char *flagstr;
447
448         flagstr = strchr(ptr, ':');
449         *flags = 0;
450         if (flagstr) {
451                 *flagstr++ = '\0';
452                 if (strlen(flagstr) == 5 && strcmp(flagstr, "aptpl") == 0)
453                         *flags = MPATH_F_APTPL_MASK;
454         }
455         return parse_prkey(ptr, prkey);
456 }
457
458 int safe_write(int fd, const void *buf, size_t count)
459 {
460         while (count > 0) {
461                 ssize_t r = write(fd, buf, count);
462                 if (r < 0) {
463                         if (errno == EINTR)
464                                 continue;
465                         return -errno;
466                 }
467                 count -= r;
468                 buf = (const char *)buf + r;
469         }
470         return 0;
471 }
472
473 void set_max_fds(int max_fds)
474 {
475         struct rlimit fd_limit;
476
477         if (!max_fds)
478                 return;
479
480         if (getrlimit(RLIMIT_NOFILE, &fd_limit) < 0) {
481                 condlog(0, "can't get open fds limit: %s",
482                         strerror(errno));
483                 fd_limit.rlim_cur = 0;
484                 fd_limit.rlim_max = 0;
485         }
486         if (fd_limit.rlim_cur < max_fds) {
487                 fd_limit.rlim_cur = max_fds;
488                 if (fd_limit.rlim_max < max_fds)
489                         fd_limit.rlim_max = max_fds;
490                 if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) {
491                         condlog(0, "can't set open fds limit to "
492                                 "%lu/%lu : %s",
493                                 fd_limit.rlim_cur, fd_limit.rlim_max,
494                                 strerror(errno));
495                 } else {
496                         condlog(3, "set open fds limit to %lu/%lu",
497                                 fd_limit.rlim_cur, fd_limit.rlim_max);
498                 }
499         }
500 }
501
502 void free_scandir_result(struct scandir_result *res)
503 {
504         int i;
505
506         for (i = 0; i < res->n; i++)
507                 FREE(res->di[i]);
508         FREE(res->di);
509 }
510
511 void close_fd(void *arg)
512 {
513         close((long)arg);
514 }