d4b94ee6dc7fcc1c95b37b6f16270084336d8028
[platform/upstream/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                         ftruncate(fd, 0);
169                         goto fail;
170                 }
171                 fsync(fd);
172                 condlog(3, "Initialized new bindings file [%s]", file);
173         }
174
175         return fd;
176
177 fail:
178         close(fd);
179         return -1;
180 }
181
182 static int
183 format_devname(char *name, int id, int len, char *prefix)
184 {
185         int pos;
186         int prefix_len = strlen(prefix);
187
188         memset(name,0, len);
189         strcpy(name, prefix);
190         for (pos = len - 1; pos >= prefix_len; pos--) {
191                 name[pos] = 'a' + id % 26;
192                 if (id < 26)
193                         break;
194                 id /= 26;
195                 id--;
196         }
197         memmove(name + prefix_len, name + pos, len - pos);
198         name[prefix_len + len - pos] = '\0';
199         return (prefix_len + len - pos);
200 }
201
202 static int
203 scan_devname(char *alias, char *prefix)
204 {
205         char *c;
206         int i, n = 0;
207
208         if (!prefix || strncmp(alias, prefix, strlen(prefix)))
209                 return -1;
210
211         c = alias + strlen(prefix);
212         while (*c != '\0' && *c != ' ' && *c != '\t') {
213                 i = *c - 'a';
214                 n = ( n * 26 ) + i;
215                 c++;
216                 if (*c < 'a' || *c > 'z')
217                         break;
218                 n++;
219         }
220
221         return n;
222 }
223
224 static int
225 lookup_binding(FILE *f, char *map_wwid, char **map_alias, char *prefix)
226 {
227         char buf[LINE_MAX];
228         unsigned int line_nr = 0;
229         int id = 0;
230
231         *map_alias = NULL;
232
233         while (fgets(buf, LINE_MAX, f)) {
234                 char *c, *alias, *wwid;
235                 int curr_id;
236
237                 line_nr++;
238                 c = strpbrk(buf, "#\n\r");
239                 if (c)
240                         *c = '\0';
241                 alias = strtok(buf, " \t");
242                 if (!alias) /* blank line */
243                         continue;
244                 curr_id = scan_devname(alias, prefix);
245                 if (curr_id >= id)
246                         id = curr_id + 1;
247                 wwid = strtok(NULL, "");
248                 if (!wwid){
249                         condlog(3,
250                                 "Ignoring malformed line %u in bindings file",
251                                 line_nr);
252                         continue;
253                 }
254                 if (strcmp(wwid, map_wwid) == 0){
255                         condlog(3, "Found matching wwid [%s] in bindings file."
256                                 " Setting alias to %s", wwid, alias);
257                         *map_alias = strdup(alias);
258                         if (*map_alias == NULL)
259                                 condlog(0, "Cannot copy alias from bindings "
260                                         "file : %s", strerror(errno));
261                         return id;
262                 }
263         }
264         condlog(3, "No matching wwid [%s] in bindings file.", map_wwid);
265         return id;
266 }
267
268 static int
269 rlookup_binding(FILE *f, char **map_wwid, char *map_alias)
270 {
271         char buf[LINE_MAX];
272         unsigned int line_nr = 0;
273         int id = 0;
274
275         *map_wwid = NULL;
276
277         while (fgets(buf, LINE_MAX, f)) {
278                 char *c, *alias, *wwid;
279                 int curr_id;
280
281                 line_nr++;
282                 c = strpbrk(buf, "#\n\r");
283                 if (c)
284                         *c = '\0';
285                 alias = strtok(buf, " \t");
286                 if (!alias) /* blank line */
287                         continue;
288                 curr_id = scan_devname(alias, NULL); /* TBD: Why this call? */
289                 if (curr_id >= id)
290                         id = curr_id + 1;
291                 wwid = strtok(NULL, " \t");
292                 if (!wwid){
293                         condlog(3,
294                                 "Ignoring malformed line %u in bindings file",
295                                 line_nr);
296                         continue;
297                 }
298                 if (strcmp(alias, map_alias) == 0){
299                         condlog(3, "Found matching alias [%s] in bindings file."
300                                 "\nSetting wwid to %s", alias, wwid);
301                         *map_wwid = strdup(wwid);
302                         if (*map_wwid == NULL)
303                                 condlog(0, "Cannot copy alias from bindings "
304                                         "file : %s", strerror(errno));
305                         return id;
306                 }
307         }
308         condlog(3, "No matching alias [%s] in bindings file.", map_alias);
309         return id;
310 }
311
312 static char *
313 allocate_binding(int fd, char *wwid, int id, char *prefix)
314 {
315         char buf[LINE_MAX];
316         off_t offset;
317         char *alias, *c;
318         int i;
319
320         if (id < 0) {
321                 condlog(0, "Bindings file full. Cannot allocate new binding");
322                 return NULL;
323         }
324
325         i = format_devname(buf, id, LINE_MAX, prefix);
326         c = buf + i;
327         snprintf(c,LINE_MAX - i, " %s\n", wwid);
328         buf[LINE_MAX - 1] = '\0';
329
330         offset = lseek(fd, 0, SEEK_END);
331         if (offset < 0){
332                 condlog(0, "Cannot seek to end of bindings file : %s",
333                         strerror(errno));
334                 return NULL;
335         }
336         if (write_all(fd, buf, strlen(buf)) != strlen(buf)){
337                 condlog(0, "Cannot write binding to bindings file : %s",
338                         strerror(errno));
339                 /* clear partial write */
340                 ftruncate(fd, offset);
341                 return NULL;
342         }
343         c = strchr(buf, ' ');
344         *c = '\0';
345         alias = strdup(buf);
346         if (alias == NULL)
347                 condlog(0, "cannot copy new alias from bindings file : %s",
348                         strerror(errno));
349         else
350                 condlog(3, "Created new binding [%s] for WWID [%s]", alias,
351                         wwid);
352         return alias;
353 }
354
355 char *
356 get_user_friendly_alias(char *wwid, char *file, char *prefix)
357 {
358         char *alias;
359         int fd, scan_fd, id;
360         FILE *f;
361         int can_write;
362
363         if (!wwid || *wwid == '\0') {
364                 condlog(3, "Cannot find binding for empty WWID");
365                 return NULL;
366         }
367
368         fd = open_bindings_file(file, &can_write);
369         if (fd < 0)
370                 return NULL;
371
372         scan_fd = dup(fd);
373         if (scan_fd < 0) {
374                 condlog(0, "Cannot dup bindings file descriptor : %s",
375                         strerror(errno));
376                 close(fd);
377                 return NULL;
378         }
379
380         f = fdopen(scan_fd, "r");
381         if (!f) {
382                 condlog(0, "cannot fdopen on bindings file descriptor : %s",
383                         strerror(errno));
384                 close(scan_fd);
385                 close(fd);
386                 return NULL;
387         }
388
389         id = lookup_binding(f, wwid, &alias, prefix);
390         if (id < 0) {
391                 fclose(f);
392                 close(scan_fd);
393                 close(fd);
394                 return NULL;
395         }
396
397         if (!alias && can_write)
398                 alias = allocate_binding(fd, wwid, id, prefix);
399
400         fclose(f);
401         close(scan_fd);
402         close(fd);
403         return alias;
404 }
405
406 char *
407 get_user_friendly_wwid(char *alias, char *file)
408 {
409         char *wwid;
410         int fd, scan_fd, id, unused;
411         FILE *f;
412
413         if (!alias || *alias == '\0') {
414                 condlog(3, "Cannot find binding for empty alias");
415                 return NULL;
416         }
417
418         fd = open_bindings_file(file, &unused);
419         if (fd < 0)
420                 return NULL;
421
422         scan_fd = dup(fd);
423         if (scan_fd < 0) {
424                 condlog(0, "Cannot dup bindings file descriptor : %s",
425                         strerror(errno));
426                 close(fd);
427                 return NULL;
428         }
429
430         f = fdopen(scan_fd, "r");
431         if (!f) {
432                 condlog(0, "cannot fdopen on bindings file descriptor : %s",
433                         strerror(errno));
434                 close(scan_fd);
435                 close(fd);
436                 return NULL;
437         }
438
439         id = rlookup_binding(f, &wwid, alias);
440         if (id < 0) {
441                 fclose(f);
442                 close(scan_fd);
443                 close(fd);
444                 return NULL;
445         }
446
447         fclose(f);
448         close(scan_fd);
449         close(fd);
450         return wwid;
451 }