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