libmultipath: remove max_fds code duplication
[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(4, "*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                 if (lstat(block_path, &statbuf) == 0) {
180                         if (S_ISLNK(statbuf.st_mode) &&
181                             readlink(block_path, dev, FILE_NAME_SIZE-1) > 0) {
182                                 char *p = strrchr(dev, '/');
183
184                                 if (!p) {
185                                         condlog(0, "No sysfs entry for %s",
186                                                 block_path);
187                                         return 1;
188                                 }
189                                 p++;
190                                 strncpy(devname, p, devname_len);
191                                 return 0;
192                         }
193                 }
194                 goto skip_proc;
195         }
196         memset(block_path, 0, sizeof(block_path));
197
198         if (!(fd = fopen("/proc/partitions", "r"))) {
199                 condlog(0, "Cannot open /proc/partitions");
200                 return 1;
201         }
202
203         while (!feof(fd)) {
204                 int r = fscanf(fd,"%u %u %*d %s",&tmpmaj, &tmpmin, dev);
205                 if (!r) {
206                         r = fscanf(fd,"%*s\n");
207                         continue;
208                 }
209                 if (r != 3)
210                         continue;
211
212                 if ((major == tmpmaj) && (minor == tmpmin)) {
213                         if (snprintf(block_path, sizeof(block_path),
214                                      "/sys/block/%s", dev) >= sizeof(block_path)) {
215                                 condlog(0, "device name %s is too long", dev);
216                                 fclose(fd);
217                                 return 1;
218                         }
219                         break;
220                 }
221         }
222         fclose(fd);
223 skip_proc:
224         if (strncmp(block_path,"/sys/block", 10)) {
225                 condlog(3, "No device found for %u:%u", major, minor);
226                 return 1;
227         }
228
229         if (stat(block_path, &statbuf) < 0) {
230                 condlog(0, "No sysfs entry for %s", block_path);
231                 return 1;
232         }
233
234         if (S_ISDIR(statbuf.st_mode) == 0) {
235                 condlog(0, "sysfs entry %s is not a directory", block_path);
236                 return 1;
237         }
238         basenamecpy((const char *)block_path, devname, devname_len);
239         return 0;
240 }
241
242 /* This function returns a pointer inside of the supplied pathname string.
243  * If is_path_device is true, it may also modify the supplied string */
244 char *convert_dev(char *name, int is_path_device)
245 {
246         char *ptr;
247
248         if (!name)
249                 return NULL;
250         if (is_path_device) {
251                 ptr = strstr(name, "cciss/");
252                 if (ptr) {
253                         ptr += 5;
254                         *ptr = '!';
255                 }
256         }
257         if (!strncmp(name, "/dev/", 5) && strlen(name) > 5)
258                 ptr = name + 5;
259         else
260                 ptr = name;
261         return ptr;
262 }
263
264 dev_t parse_devt(const char *dev_t)
265 {
266         int maj, min;
267
268         if (sscanf(dev_t,"%d:%d", &maj, &min) != 2)
269                 return 0;
270
271         return makedev(maj, min);
272 }
273
274 char *parse_uid_attribute_by_attrs(char *uid_attrs, char *path_dev)
275 {
276         char *uid_attribute;
277         char *uid_attr_record;
278         char *dev;
279         char *attr;
280         char *tmp;
281         int  count;
282
283         if(!uid_attrs || !path_dev)
284                 return NULL;
285
286         count = get_word(uid_attrs, &uid_attr_record);
287         while (uid_attr_record) {
288                 tmp = strrchr(uid_attr_record, ':');
289                 if (!tmp) {
290                         free(uid_attr_record);
291                         if (!count)
292                                 break;
293                         uid_attrs += count;
294                         count = get_word(uid_attrs, &uid_attr_record);
295                         continue;
296                 }
297                 dev = uid_attr_record;
298                 attr = tmp + 1;
299                 *tmp = '\0';
300
301                 if(!strncmp(path_dev, dev, strlen(dev))) {
302                         uid_attribute = STRDUP(attr);
303                         free(uid_attr_record);
304                         return uid_attribute;
305                 }
306
307                 free(uid_attr_record);
308                 if (!count)
309                         break;
310                 uid_attrs += count;
311                 count = get_word(uid_attrs, &uid_attr_record);
312         }
313         return NULL;
314 }
315
316 void
317 setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached)
318 {
319         int ret;
320
321         ret = pthread_attr_init(attr);
322         assert(ret == 0);
323         if (stacksize < PTHREAD_STACK_MIN)
324                 stacksize = PTHREAD_STACK_MIN;
325         ret = pthread_attr_setstacksize(attr, stacksize);
326         assert(ret == 0);
327         if (detached) {
328                 ret = pthread_attr_setdetachstate(attr,
329                                                   PTHREAD_CREATE_DETACHED);
330                 assert(ret == 0);
331         }
332 }
333
334 int systemd_service_enabled_in(const char *dev, const char *prefix)
335 {
336         char path[PATH_SIZE], file[PATH_MAX], service[PATH_SIZE];
337         DIR *dirfd;
338         struct dirent *d;
339         int found = 0;
340
341         snprintf(service, PATH_SIZE, "multipathd.service");
342         snprintf(path, PATH_SIZE, "%s/systemd/system", prefix);
343         condlog(3, "%s: checking for %s in %s", dev, service, path);
344
345         dirfd = opendir(path);
346         if (dirfd == NULL)
347                 return 0;
348
349         while ((d = readdir(dirfd)) != NULL) {
350                 char *p;
351                 struct stat stbuf;
352
353                 if ((strcmp(d->d_name,".") == 0) ||
354                     (strcmp(d->d_name,"..") == 0))
355                         continue;
356
357                 if (strlen(d->d_name) < 6)
358                         continue;
359
360                 p = d->d_name + strlen(d->d_name) - 6;
361                 if (strcmp(p, ".wants"))
362                         continue;
363                 snprintf(file, sizeof(file), "%s/%s/%s",
364                          path, d->d_name, service);
365                 if (stat(file, &stbuf) == 0) {
366                         condlog(3, "%s: found %s", dev, file);
367                         found++;
368                         break;
369                 }
370         }
371         closedir(dirfd);
372
373         return found;
374 }
375
376 int systemd_service_enabled(const char *dev)
377 {
378         int found = 0;
379
380         found = systemd_service_enabled_in(dev, "/etc");
381         if (!found)
382                 found = systemd_service_enabled_in(dev, "/usr/lib");
383         if (!found)
384                 found = systemd_service_enabled_in(dev, "/lib");
385         if (!found)
386                 found = systemd_service_enabled_in(dev, "/run");
387         return found;
388 }
389
390 static int _linux_version_code;
391 static pthread_once_t _lvc_initialized = PTHREAD_ONCE_INIT;
392
393 /* Returns current kernel version encoded as major*65536 + minor*256 + patch,
394  * so, for example,  to check if the kernel is greater than 2.2.11:
395  *
396  *     if (get_linux_version_code() > KERNEL_VERSION(2,2,11)) { <stuff> }
397  *
398  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
399  * Code copied from busybox (GPLv2 or later)
400  */
401 static void
402 _set_linux_version_code(void)
403 {
404         struct utsname name;
405         char *t;
406         int i, r;
407
408         uname(&name); /* never fails */
409         t = name.release;
410         r = 0;
411         for (i = 0; i < 3; i++) {
412                 t = strtok(t, ".");
413                 r = r * 256 + (t ? atoi(t) : 0);
414                 t = NULL;
415         }
416         _linux_version_code = r;
417 }
418
419 int get_linux_version_code(void)
420 {
421         pthread_once(&_lvc_initialized, _set_linux_version_code);
422         return _linux_version_code;
423 }
424
425 int parse_prkey(char *ptr, uint64_t *prkey)
426 {
427         if (!ptr)
428                 return 1;
429         if (*ptr == '0')
430                 ptr++;
431         if (*ptr == 'x' || *ptr == 'X')
432                 ptr++;
433         if (*ptr == '\0' || strlen(ptr) > 16)
434                 return 1;
435         if (strlen(ptr) != strspn(ptr, "0123456789aAbBcCdDeEfF"))
436                 return 1;
437         if (sscanf(ptr, "%" SCNx64 "", prkey) != 1)
438                 return 1;
439         return 0;
440 }
441
442 int parse_prkey_flags(char *ptr, uint64_t *prkey, uint8_t *flags)
443 {
444         char *flagstr;
445
446         flagstr = strchr(ptr, ':');
447         *flags = 0;
448         if (flagstr) {
449                 *flagstr++ = '\0';
450                 if (strlen(flagstr) == 5 && strcmp(flagstr, "aptpl") == 0)
451                         *flags = MPATH_F_APTPL_MASK;
452         }
453         return parse_prkey(ptr, prkey);
454 }
455
456 int safe_write(int fd, const void *buf, size_t count)
457 {
458         while (count > 0) {
459                 ssize_t r = write(fd, buf, count);
460                 if (r < 0) {
461                         if (errno == EINTR)
462                                 continue;
463                         return -errno;
464                 }
465                 count -= r;
466                 buf = (const char *)buf + r;
467         }
468         return 0;
469 }
470
471 void set_max_fds(int max_fds)
472 {
473         struct rlimit fd_limit;
474
475         if (!max_fds)
476                 return;
477
478         if (getrlimit(RLIMIT_NOFILE, &fd_limit) < 0) {
479                 condlog(0, "can't get open fds limit: %s",
480                         strerror(errno));
481                 fd_limit.rlim_cur = 0;
482                 fd_limit.rlim_max = 0;
483         }
484         if (fd_limit.rlim_cur < max_fds) {
485                 fd_limit.rlim_cur = max_fds;
486                 if (fd_limit.rlim_max < max_fds)
487                         fd_limit.rlim_max = max_fds;
488                 if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) {
489                         condlog(0, "can't set open fds limit to "
490                                 "%lu/%lu : %s",
491                                 fd_limit.rlim_cur, fd_limit.rlim_max,
492                                 strerror(errno));
493                 } else {
494                         condlog(3, "set open fds limit to %lu/%lu",
495                                 fd_limit.rlim_cur, fd_limit.rlim_max);
496                 }
497         }
498 }