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