multipath: Fix warnings from stricter compile options.
[multipath-tools/.git] / libmultipath / alias.c
1 /*
2  * Copyright (c) 2005 Christophe Varoqui
3  * Copyright (c) 2005 Benjamin Marzinski, Redhat
4  */
5 #include <stdlib.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9 #include <errno.h>
10 #include <unistd.h>
11 #include <string.h>
12 #include <limits.h>
13 #include <stdio.h>
14 #include <signal.h>
15
16 #include "debug.h"
17 #include "uxsock.h"
18 #include "alias.h"
19
20
21 /*
22  * significant parts of this file were taken from iscsi-bindings.c of the
23  * linux-iscsi project.
24  * Copyright (C) 2002 Cisco Systems, Inc.
25  *
26  * This program is free software; you can redistribute it and/or modify
27  * it under the terms of the GNU General Public License as published
28  * by the Free Software Foundation; either version 2 of the License, or
29  * (at your option) any later version.
30  *
31  * This program is distributed in the hope that it will be useful, but
32  * WITHOUT ANY WARRANTY; without even the implied warranty of
33  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
34  * General Public License for more details.
35  *
36  * See the file COPYING included with this distribution for more details.
37  */
38
39 static int
40 ensure_directories_exist(char *str, mode_t dir_mode)
41 {
42         char *pathname;
43         char *end;
44         int err;
45
46         pathname = strdup(str);
47         if (!pathname){
48                 condlog(0, "Cannot copy bindings file pathname : %s",
49                         strerror(errno));
50                 return -1;
51         }
52         end = pathname;
53         /* skip leading slashes */
54         while (end && *end && (*end == '/'))
55                 end++;
56
57         while ((end = strchr(end, '/'))) {
58                 /* if there is another slash, make the dir. */
59                 *end = '\0';
60                 err = mkdir(pathname, dir_mode);
61                 if (err && errno != EEXIST) {
62                         condlog(0, "Cannot make directory [%s] : %s",
63                                 pathname, strerror(errno));
64                         free(pathname);
65                         return -1;
66                 }
67                 if (!err)
68                         condlog(3, "Created dir [%s]", pathname);
69                 *end = '/';
70                 end++;
71         }
72         free(pathname);
73         return 0;
74 }
75
76 static void
77 sigalrm(int sig)
78 {
79         /* do nothing */
80 }
81
82 static int
83 lock_bindings_file(int fd)
84 {
85         struct sigaction act, oldact;
86         sigset_t set, oldset;
87         struct flock lock;
88         int err;
89
90         memset(&lock, 0, sizeof(lock));
91         lock.l_type = F_WRLCK;
92         lock.l_whence = SEEK_SET;
93
94         act.sa_handler = sigalrm;
95         sigemptyset(&act.sa_mask);
96         act.sa_flags = 0;
97         sigemptyset(&set);
98         sigaddset(&set, SIGALRM);
99
100         sigaction(SIGALRM, &act, &oldact);
101         sigprocmask(SIG_UNBLOCK, &set, &oldset);
102
103         alarm(BINDINGS_FILE_TIMEOUT);
104         err = fcntl(fd, F_SETLKW, &lock);
105         alarm(0);
106
107         if (err) {
108                 if (errno != EINTR)
109                         condlog(0, "Cannot lock bindings file : %s",
110                                         strerror(errno));
111                 else
112                         condlog(0, "Bindings file is locked. Giving up.");
113         }
114
115         sigprocmask(SIG_SETMASK, &oldset, NULL);
116         sigaction(SIGALRM, &oldact, NULL);
117         return err;
118
119 }
120
121
122 static int
123 open_bindings_file(char *file, int *can_write)
124 {
125         int fd;
126         struct stat s;
127
128         if (ensure_directories_exist(file, 0700))
129                 return -1;
130         *can_write = 1;
131         fd = open(file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
132         if (fd < 0) {
133                 if (errno == EROFS) {
134                         *can_write = 0;
135                         condlog(3, "Cannot open bindings file [%s] read/write. "
136                                 " trying readonly", file);
137                         fd = open(file, O_RDONLY);
138                         if (fd < 0) {
139                                 condlog(0, "Cannot open bindings file [%s] "
140                                         "readonly : %s", file, strerror(errno));
141                                 return -1;
142                         }
143                 }
144                 else {
145                         condlog(0, "Cannot open bindings file [%s] : %s", file,
146                                 strerror(errno));
147                         return -1;
148                 }
149         }
150         if (*can_write && lock_bindings_file(fd) < 0)
151                 goto fail;
152
153         memset(&s, 0, sizeof(s));
154         if (fstat(fd, &s) < 0){
155                 condlog(0, "Cannot stat bindings file : %s", strerror(errno));
156                 goto fail;
157         }
158         if (s.st_size == 0) {
159                 if (*can_write == 0)
160                         goto fail;
161                 /* If bindings file is empty, write the header */
162                 size_t len = strlen(BINDINGS_FILE_HEADER);
163                 if (write_all(fd, BINDINGS_FILE_HEADER, len) != len) {
164                         condlog(0,
165                                 "Cannot write header to bindings file : %s",
166                                 strerror(errno));
167                         /* cleanup partially written header */
168                         if (ftruncate(fd, 0))
169                                 condlog(0, "Cannot truncate the header : %s",
170                                         strerror(errno));
171                         goto fail;
172                 }
173                 fsync(fd);
174                 condlog(3, "Initialized new bindings file [%s]", file);
175         }
176
177         return fd;
178
179 fail:
180         close(fd);
181         return -1;
182 }
183
184 static int
185 format_devname(char *name, int id, int len, char *prefix)
186 {
187         int pos;
188         int prefix_len = strlen(prefix);
189
190         memset(name,0, len);
191         strcpy(name, prefix);
192         for (pos = len - 1; pos >= prefix_len; pos--) {
193                 name[pos] = 'a' + id % 26;
194                 if (id < 26)
195                         break;
196                 id /= 26;
197                 id--;
198         }
199         memmove(name + prefix_len, name + pos, len - pos);
200         name[prefix_len + len - pos] = '\0';
201         return (prefix_len + len - pos);
202 }
203
204 static int
205 scan_devname(char *alias, char *prefix)
206 {
207         char *c;
208         int i, n = 0;
209
210         if (!prefix || strncmp(alias, prefix, strlen(prefix)))
211                 return -1;
212
213         c = alias + strlen(prefix);
214         while (*c != '\0' && *c != ' ' && *c != '\t') {
215                 i = *c - 'a';
216                 n = ( n * 26 ) + i;
217                 c++;
218                 if (*c < 'a' || *c > 'z')
219                         break;
220                 n++;
221         }
222
223         return n;
224 }
225
226 static int
227 lookup_binding(FILE *f, char *map_wwid, char **map_alias, char *prefix)
228 {
229         char buf[LINE_MAX];
230         unsigned int line_nr = 0;
231         int id = 0;
232
233         *map_alias = NULL;
234
235         while (fgets(buf, LINE_MAX, f)) {
236                 char *c, *alias, *wwid;
237                 int curr_id;
238
239                 line_nr++;
240                 c = strpbrk(buf, "#\n\r");
241                 if (c)
242                         *c = '\0';
243                 alias = strtok(buf, " \t");
244                 if (!alias) /* blank line */
245                         continue;
246                 curr_id = scan_devname(alias, prefix);
247                 if (curr_id >= id)
248                         id = curr_id + 1;
249                 wwid = strtok(NULL, "");
250                 if (!wwid){
251                         condlog(3,
252                                 "Ignoring malformed line %u in bindings file",
253                                 line_nr);
254                         continue;
255                 }
256                 if (strcmp(wwid, map_wwid) == 0){
257                         condlog(3, "Found matching wwid [%s] in bindings file."
258                                 " Setting alias to %s", wwid, alias);
259                         *map_alias = strdup(alias);
260                         if (*map_alias == NULL)
261                                 condlog(0, "Cannot copy alias from bindings "
262                                         "file : %s", strerror(errno));
263                         return id;
264                 }
265         }
266         condlog(3, "No matching wwid [%s] in bindings file.", map_wwid);
267         return id;
268 }
269
270 static int
271 rlookup_binding(FILE *f, char **map_wwid, char *map_alias)
272 {
273         char buf[LINE_MAX];
274         unsigned int line_nr = 0;
275         int id = 0;
276
277         *map_wwid = NULL;
278
279         while (fgets(buf, LINE_MAX, f)) {
280                 char *c, *alias, *wwid;
281                 int curr_id;
282
283                 line_nr++;
284                 c = strpbrk(buf, "#\n\r");
285                 if (c)
286                         *c = '\0';
287                 alias = strtok(buf, " \t");
288                 if (!alias) /* blank line */
289                         continue;
290                 curr_id = scan_devname(alias, NULL); /* TBD: Why this call? */
291                 if (curr_id >= id)
292                         id = curr_id + 1;
293                 wwid = strtok(NULL, " \t");
294                 if (!wwid){
295                         condlog(3,
296                                 "Ignoring malformed line %u in bindings file",
297                                 line_nr);
298                         continue;
299                 }
300                 if (strcmp(alias, map_alias) == 0){
301                         condlog(3, "Found matching alias [%s] in bindings file."
302                                 "\nSetting wwid to %s", alias, wwid);
303                         *map_wwid = strdup(wwid);
304                         if (*map_wwid == NULL)
305                                 condlog(0, "Cannot copy alias from bindings "
306                                         "file : %s", strerror(errno));
307                         return id;
308                 }
309         }
310         condlog(3, "No matching alias [%s] in bindings file.", map_alias);
311         return id;
312 }
313
314 static char *
315 allocate_binding(int fd, char *wwid, int id, char *prefix)
316 {
317         char buf[LINE_MAX];
318         off_t offset;
319         char *alias, *c;
320         int i;
321
322         if (id < 0) {
323                 condlog(0, "Bindings file full. Cannot allocate new binding");
324                 return NULL;
325         }
326
327         i = format_devname(buf, id, LINE_MAX, prefix);
328         c = buf + i;
329         snprintf(c,LINE_MAX - i, " %s\n", wwid);
330         buf[LINE_MAX - 1] = '\0';
331
332         offset = lseek(fd, 0, SEEK_END);
333         if (offset < 0){
334                 condlog(0, "Cannot seek to end of bindings file : %s",
335                         strerror(errno));
336                 return NULL;
337         }
338         if (write_all(fd, buf, strlen(buf)) != strlen(buf)){
339                 condlog(0, "Cannot write binding to bindings file : %s",
340                         strerror(errno));
341                 /* clear partial write */
342                 if (ftruncate(fd, offset))
343                         condlog(0, "Cannot truncate the header : %s",
344                                 strerror(errno));
345                 return NULL;
346         }
347         c = strchr(buf, ' ');
348         *c = '\0';
349         alias = strdup(buf);
350         if (alias == NULL)
351                 condlog(0, "cannot copy new alias from bindings file : %s",
352                         strerror(errno));
353         else
354                 condlog(3, "Created new binding [%s] for WWID [%s]", alias,
355                         wwid);
356         return alias;
357 }
358
359 char *
360 get_user_friendly_alias(char *wwid, char *file, char *prefix,
361                         int bindings_read_only)
362 {
363         char *alias;
364         int fd, scan_fd, id;
365         FILE *f;
366         int can_write;
367
368         if (!wwid || *wwid == '\0') {
369                 condlog(3, "Cannot find binding for empty WWID");
370                 return NULL;
371         }
372
373         fd = open_bindings_file(file, &can_write);
374         if (fd < 0)
375                 return NULL;
376
377         scan_fd = dup(fd);
378         if (scan_fd < 0) {
379                 condlog(0, "Cannot dup bindings file descriptor : %s",
380                         strerror(errno));
381                 close(fd);
382                 return NULL;
383         }
384
385         f = fdopen(scan_fd, "r");
386         if (!f) {
387                 condlog(0, "cannot fdopen on bindings file descriptor : %s",
388                         strerror(errno));
389                 close(scan_fd);
390                 close(fd);
391                 return NULL;
392         }
393
394         id = lookup_binding(f, wwid, &alias, prefix);
395         if (id < 0) {
396                 fclose(f);
397                 close(scan_fd);
398                 close(fd);
399                 return NULL;
400         }
401
402         if (!alias && can_write && !bindings_read_only)
403                 alias = allocate_binding(fd, wwid, id, prefix);
404
405         fclose(f);
406         close(scan_fd);
407         close(fd);
408         return alias;
409 }
410
411 char *
412 get_user_friendly_wwid(char *alias, char *file)
413 {
414         char *wwid;
415         int fd, scan_fd, id, unused;
416         FILE *f;
417
418         if (!alias || *alias == '\0') {
419                 condlog(3, "Cannot find binding for empty alias");
420                 return NULL;
421         }
422
423         fd = open_bindings_file(file, &unused);
424         if (fd < 0)
425                 return NULL;
426
427         scan_fd = dup(fd);
428         if (scan_fd < 0) {
429                 condlog(0, "Cannot dup bindings file descriptor : %s",
430                         strerror(errno));
431                 close(fd);
432                 return NULL;
433         }
434
435         f = fdopen(scan_fd, "r");
436         if (!f) {
437                 condlog(0, "cannot fdopen on bindings file descriptor : %s",
438                         strerror(errno));
439                 close(scan_fd);
440                 close(fd);
441                 return NULL;
442         }
443
444         id = rlookup_binding(f, &wwid, alias);
445         if (id < 0) {
446                 fclose(f);
447                 close(scan_fd);
448                 close(fd);
449                 return NULL;
450         }
451
452         fclose(f);
453         close(scan_fd);
454         close(fd);
455         return wwid;
456 }