multipath-tools: add alias_prefix to multipath.conf.5
[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 <errno.h>
7 #include <unistd.h>
8 #include <string.h>
9 #include <limits.h>
10 #include <stdio.h>
11
12 #include "debug.h"
13 #include "uxsock.h"
14 #include "alias.h"
15 #include "file.h"
16 #include "vector.h"
17 #include "checkers.h"
18 #include "structs.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 int
40 valid_alias(char *alias)
41 {
42         if (strchr(alias, '/') != NULL)
43                 return 0;
44         return 1;
45 }
46
47
48 static int
49 format_devname(char *name, int id, int len, char *prefix)
50 {
51         int pos;
52         int prefix_len = strlen(prefix);
53
54         memset(name,0, len);
55         strcpy(name, prefix);
56         for (pos = len - 1; pos >= prefix_len; pos--) {
57                 id--;
58                 name[pos] = 'a' + id % 26;
59                 if (id < 26)
60                         break;
61                 id /= 26;
62         }
63         memmove(name + prefix_len, name + pos, len - pos);
64         name[prefix_len + len - pos] = '\0';
65         return (prefix_len + len - pos);
66 }
67
68 static int
69 scan_devname(char *alias, char *prefix)
70 {
71         char *c;
72         int i, n = 0;
73
74         if (!prefix || strncmp(alias, prefix, strlen(prefix)))
75                 return -1;
76
77         if (strlen(alias) == strlen(prefix))
78                 return -1;
79
80         if (strlen(alias) > strlen(prefix) + 7)
81                 /* id of 'aaaaaaaa' overflows int */
82                 return -1;
83
84         c = alias + strlen(prefix);
85         while (*c != '\0' && *c != ' ' && *c != '\t') {
86                 if (*c < 'a' || *c > 'z')
87                         return -1;
88                 i = *c - 'a';
89                 n = ( n * 26 ) + i;
90                 if (n < 0)
91                         return -1;
92                 c++;
93                 n++;
94         }
95
96         return n;
97 }
98
99 static int
100 lookup_binding(FILE *f, char *map_wwid, char **map_alias, char *prefix)
101 {
102         char buf[LINE_MAX];
103         unsigned int line_nr = 0;
104         int id = 1;
105         int biggest_id = 1;
106         int smallest_bigger_id = INT_MAX;
107
108         *map_alias = NULL;
109
110         while (fgets(buf, LINE_MAX, f)) {
111                 char *c, *alias, *wwid;
112                 int curr_id;
113
114                 line_nr++;
115                 c = strpbrk(buf, "#\n\r");
116                 if (c)
117                         *c = '\0';
118                 alias = strtok(buf, " \t");
119                 if (!alias) /* blank line */
120                         continue;
121                 curr_id = scan_devname(alias, prefix);
122                 if (curr_id == id)
123                         id++;
124                 if (curr_id > biggest_id)
125                         biggest_id = curr_id;
126                 if (curr_id > id && curr_id < smallest_bigger_id)
127                         smallest_bigger_id = curr_id;
128                 wwid = strtok(NULL, " \t");
129                 if (!wwid){
130                         condlog(3,
131                                 "Ignoring malformed line %u in bindings file",
132                                 line_nr);
133                         continue;
134                 }
135                 if (strcmp(wwid, map_wwid) == 0){
136                         condlog(3, "Found matching wwid [%s] in bindings file."
137                                 " Setting alias to %s", wwid, alias);
138                         *map_alias = strdup(alias);
139                         if (*map_alias == NULL)
140                                 condlog(0, "Cannot copy alias from bindings "
141                                         "file : %s", strerror(errno));
142                         return 0;
143                 }
144         }
145         condlog(3, "No matching wwid [%s] in bindings file.", map_wwid);
146         if (id < 0) {
147                 condlog(0, "no more available user_friendly_names");
148                 return 0;
149         }
150         if (id < smallest_bigger_id)
151                 return id;
152         return biggest_id + 1;
153 }
154
155 static int
156 rlookup_binding(FILE *f, char *buff, char *map_alias, char *prefix)
157 {
158         char line[LINE_MAX];
159         unsigned int line_nr = 0;
160
161         buff[0] = '\0';
162
163         while (fgets(line, LINE_MAX, f)) {
164                 char *c, *alias, *wwid;
165
166                 line_nr++;
167                 c = strpbrk(line, "#\n\r");
168                 if (c)
169                         *c = '\0';
170                 alias = strtok(line, " \t");
171                 if (!alias) /* blank line */
172                         continue;
173                 wwid = strtok(NULL, " \t");
174                 if (!wwid){
175                         condlog(3,
176                                 "Ignoring malformed line %u in bindings file",
177                                 line_nr);
178                         continue;
179                 }
180                 if (strlen(wwid) > WWID_SIZE - 1) {
181                         condlog(3,
182                                 "Ignoring too large wwid at %u in bindings file", line_nr);
183                         continue;
184                 }
185                 if (strcmp(alias, map_alias) == 0){
186                         condlog(3, "Found matching alias [%s] in bindings file."
187                                 "\nSetting wwid to %s", alias, wwid);
188                         strncpy(buff, wwid, WWID_SIZE);
189                         buff[WWID_SIZE - 1] = '\0';
190                         return 0;
191                 }
192         }
193         condlog(3, "No matching alias [%s] in bindings file.", map_alias);
194
195         return -1;
196 }
197
198 static char *
199 allocate_binding(int fd, char *wwid, int id, char *prefix)
200 {
201         char buf[LINE_MAX];
202         off_t offset;
203         char *alias, *c;
204         int i;
205
206         if (id < 0) {
207                 condlog(0, "Bindings file full. Cannot allocate new binding");
208                 return NULL;
209         }
210
211         i = format_devname(buf, id, LINE_MAX, prefix);
212         c = buf + i;
213         snprintf(c,LINE_MAX - i, " %s\n", wwid);
214         buf[LINE_MAX - 1] = '\0';
215
216         offset = lseek(fd, 0, SEEK_END);
217         if (offset < 0){
218                 condlog(0, "Cannot seek to end of bindings file : %s",
219                         strerror(errno));
220                 return NULL;
221         }
222         if (write_all(fd, buf, strlen(buf)) != strlen(buf)){
223                 condlog(0, "Cannot write binding to bindings file : %s",
224                         strerror(errno));
225                 /* clear partial write */
226                 if (ftruncate(fd, offset))
227                         condlog(0, "Cannot truncate the header : %s",
228                                 strerror(errno));
229                 return NULL;
230         }
231         c = strchr(buf, ' ');
232         if (c)
233                 *c = '\0';
234         alias = strdup(buf);
235         if (alias == NULL)
236                 condlog(0, "cannot copy new alias from bindings file : %s",
237                         strerror(errno));
238         else
239                 condlog(3, "Created new binding [%s] for WWID [%s]", alias,
240                         wwid);
241         return alias;
242 }
243
244 char *
245 use_existing_alias (char *wwid, char *file, char *alias_old,
246                 char *prefix, int bindings_read_only)
247 {
248         char *alias = NULL;
249         int id = 0;
250         int fd, can_write;
251         char buff[WWID_SIZE];
252         FILE *f;
253
254         fd = open_file(file, &can_write, BINDINGS_FILE_HEADER);
255         if (fd < 0)
256                 return NULL;
257
258         f = fdopen(fd, "r");
259         if (!f) {
260                 condlog(0, "cannot fdopen on bindings file descriptor");
261                 close(fd);
262                 return NULL;
263         }
264         /* lookup the binding. if it exsists, the wwid will be in buff
265          * either way, id contains the id for the alias
266          */
267         rlookup_binding(f, buff, alias_old, prefix);
268
269         if (strlen(buff) > 0) {
270                 /* if buff is our wwid, it's already
271                  * allocated correctly
272                  */
273                 if (strcmp(buff, wwid) == 0)
274                         alias = STRDUP(alias_old);
275                 else {
276                         alias = NULL;
277                         condlog(0, "alias %s already bound to wwid %s, cannot reuse",
278                                 alias_old, buff);
279                 }
280                 goto out;
281         }
282
283         /* allocate the existing alias in the bindings file */
284         id = scan_devname(alias_old, prefix);
285         if (id <= 0)
286                 goto out;
287
288         if (fflush(f) != 0) {
289                 condlog(0, "cannot fflush bindings file stream : %s",
290                         strerror(errno));
291                 goto out;
292         }
293
294         if (can_write && !bindings_read_only) {
295                 alias = allocate_binding(fd, wwid, id, prefix);
296                 condlog(0, "Allocated existing binding [%s] for WWID [%s]",
297                         alias, wwid);
298         }
299
300 out:
301         fclose(f);
302         return alias;
303 }
304
305 char *
306 get_user_friendly_alias(char *wwid, char *file, char *prefix,
307                         int bindings_read_only)
308 {
309         char *alias;
310         int fd, id;
311         FILE *f;
312         int can_write;
313
314         if (!wwid || *wwid == '\0') {
315                 condlog(3, "Cannot find binding for empty WWID");
316                 return NULL;
317         }
318
319         fd = open_file(file, &can_write, BINDINGS_FILE_HEADER);
320         if (fd < 0)
321                 return NULL;
322
323         f = fdopen(fd, "r");
324         if (!f) {
325                 condlog(0, "cannot fdopen on bindings file descriptor : %s",
326                         strerror(errno));
327                 close(fd);
328                 return NULL;
329         }
330
331         id = lookup_binding(f, wwid, &alias, prefix);
332         if (id < 0) {
333                 fclose(f);
334                 return NULL;
335         }
336
337         if (fflush(f) != 0) {
338                 condlog(0, "cannot fflush bindings file stream : %s",
339                         strerror(errno));
340                 free(alias);
341                 fclose(f);
342                 return NULL;
343         }
344
345         if (!alias && can_write && !bindings_read_only && id)
346                 alias = allocate_binding(fd, wwid, id, prefix);
347
348         fclose(f);
349         return alias;
350 }
351
352 int
353 get_user_friendly_wwid(char *alias, char *buff, char *file)
354 {
355         int fd, unused;
356         FILE *f;
357
358         if (!alias || *alias == '\0') {
359                 condlog(3, "Cannot find binding for empty alias");
360                 return -1;
361         }
362
363         fd = open_file(file, &unused, BINDINGS_FILE_HEADER);
364         if (fd < 0)
365                 return -1;
366
367         f = fdopen(fd, "r");
368         if (!f) {
369                 condlog(0, "cannot fdopen on bindings file descriptor : %s",
370                         strerror(errno));
371                 close(fd);
372                 return -1;
373         }
374
375         rlookup_binding(f, buff, alias, NULL);
376         if (!strlen(buff)) {
377                 fclose(f);
378                 return -1;
379         }
380
381         fclose(f);
382         return 0;
383 }