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