add wwids file cleanup options
[multipath-tools/.git] / libmultipath / wwids.c
1 #include <stdlib.h>
2 #include <errno.h>
3 #include <unistd.h>
4 #include <string.h>
5 #include <limits.h>
6 #include <stdio.h>
7
8 #include "checkers.h"
9 #include "vector.h"
10 #include "structs.h"
11 #include "debug.h"
12 #include "uxsock.h"
13 #include "file.h"
14 #include "wwids.h"
15 #include "defaults.h"
16 #include "config.h"
17
18 /*
19  * Copyright (c) 2010 Benjamin Marzinski, Redhat
20  */
21
22 static int
23 lookup_wwid(FILE *f, char *wwid) {
24         int c;
25         char buf[LINE_MAX];
26         int count;
27
28         while ((c = fgetc(f)) != EOF){
29                 if (c != '/') {
30                         if (fgets(buf, LINE_MAX, f) == NULL)
31                                 return 0;
32                         else
33                                 continue;
34                 }
35                 count = 0;
36                 while ((c = fgetc(f)) != '/') {
37                         if (c == EOF)
38                                 return 0;
39                         if (count >= WWID_SIZE - 1)
40                                 goto next;
41                         if (wwid[count] == '\0')
42                                 goto next;
43                         if (c != wwid[count++])
44                                 goto next;
45                 }
46                 if (wwid[count] == '\0')
47                         return 1;
48 next:
49                 if (fgets(buf, LINE_MAX, f) == NULL)
50                         return 0;
51         }
52         return 0;
53 }
54
55 static int
56 write_out_wwid(int fd, char *wwid) {
57         int ret;
58         off_t offset;
59         char buf[WWID_SIZE + 3];
60
61         ret = snprintf(buf, WWID_SIZE + 3, "/%s/\n", wwid);
62         if (ret >= (WWID_SIZE + 3) || ret < 0){
63                 condlog(0, "can't format wwid for writing (%d) : %s",
64                         ret, strerror(errno));
65                 return -1;
66         }
67         offset = lseek(fd, 0, SEEK_END);
68         if (offset < 0) {
69                 condlog(0, "can't seek to the end of wwids file : %s",
70                         strerror(errno));
71                 return -1;
72         }
73         if (write_all(fd, buf, strlen(buf)) != strlen(buf)) {
74                 condlog(0, "cannot write wwid to wwids file : %s",
75                         strerror(errno));
76                 if (ftruncate(fd, offset))
77                         condlog(0, "cannot truncate failed wwid write : %s",
78                                 strerror(errno));
79                 return -1;
80         }
81         return 1;
82 }
83
84 int
85 replace_wwids(vector mp)
86 {
87         int i, fd, can_write;
88         struct multipath * mpp;
89         size_t len;
90         int ret = -1;
91
92         fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
93         if (fd < 0)
94                 goto out;
95         if (!can_write) {
96                 condlog(0, "cannot replace wwids. wwids file is read-only");
97                 goto out_file;
98         }
99         if (ftruncate(fd, 0) < 0) {
100                 condlog(0, "cannot truncate wwids file : %s", strerror(errno));
101                 goto out_file;
102         }
103         len = strlen(WWIDS_FILE_HEADER);
104         if (write_all(fd, WWIDS_FILE_HEADER, len) != len) {
105                 condlog(0, "Can't write wwid file header : %s",
106                         strerror(errno));
107                 /* cleanup partially written header */
108                 if (ftruncate(fd, 0) < 0)
109                         condlog(0, "Cannot truncate header : %s",
110                                 strerror(errno));
111                 goto out_file;
112         }
113         if (!mp || !mp->allocated) {
114                 ret = 0;
115                 goto out_file;
116         }
117         vector_foreach_slot(mp, mpp, i) {
118                 if (write_out_wwid(fd, mpp->wwid) < 0)
119                         goto out_file;
120         }
121         ret = 0;
122 out_file:
123         close(fd);
124 out:
125         return ret;
126 }
127
128 int
129 do_remove_wwid(int fd, char *str) {
130         char buf[4097];
131         char *ptr;
132         off_t start = 0;
133         int bytes;
134
135         while (1) {
136                 if (lseek(fd, start, SEEK_SET) < 0) {
137                         condlog(0, "wwid file read lseek failed : %s",
138                                 strerror(errno));
139                         return -1;
140                 }
141                 bytes = read(fd, buf, 4096);
142                 if (bytes < 0) {
143                         if (errno == EINTR || errno == EAGAIN)
144                                 continue;
145                         condlog(0, "failed to read from wwids file : %s",
146                                 strerror(errno));
147                         return -1;
148                 }
149                 if (!bytes) /* didn't find wwid to remove */
150                         return 1;
151                 buf[bytes] = '\0';
152                 ptr = strstr(buf, str);
153                 if (ptr != NULL) {
154                         condlog(3, "found '%s'", str);
155                         if (lseek(fd, start + (ptr - buf), SEEK_SET) < 0) {
156                                 condlog(0, "write lseek failed : %s",
157                                                 strerror(errno));
158                                 return -1;
159                         }
160                         while (1) {
161                                 if (write(fd, "#", 1) < 0) {
162                                         if (errno == EINTR || errno == EAGAIN)
163                                                 continue;
164                                         condlog(0, "failed to write to wwids file : %s", strerror(errno));
165                                         return -1;
166                                 }
167                                 return 0;
168                         }
169                 }
170                 ptr = strrchr(buf, '\n');
171                 if (ptr == NULL) { /* shouldn't happen, assume it is EOF */
172                         condlog(4, "couldn't find newline, assuming end of file");
173                         return 1;
174                 }
175                 start = start + (ptr - buf) + 1;
176         }
177 }
178
179
180 int
181 remove_wwid(char *wwid) {
182         int fd, len, can_write;
183         char *str;
184         int ret = -1;
185
186         len = strlen(wwid) + 4; /* two slashes the newline and a zero byte */
187         str = malloc(len);
188         if (str == NULL) {
189                 condlog(0, "can't allocate memory to remove wwid : %s",
190                         strerror(errno));
191                 return -1;
192         }
193         if (snprintf(str, len, "/%s/\n", wwid) >= len) {
194                 condlog(0, "string overflow trying to remove wwid");
195                 goto out;
196         }
197         condlog(3, "removing line '%s' from wwids file", str);
198         fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
199         if (fd < 0)
200                 goto out;
201         if (!can_write) {
202                 condlog(0, "cannot remove wwid. wwids file is read-only");
203                 goto out_file;
204         }
205         ret = do_remove_wwid(fd, str);
206
207 out_file:
208         close(fd);
209 out:
210         free(str);
211         return ret;
212 }
213
214 int
215 check_wwids_file(char *wwid, int write_wwid)
216 {
217         int fd, can_write, found, ret;
218         FILE *f;
219         fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
220         if (fd < 0)
221                 return -1;
222
223         f = fdopen(fd, "r");
224         if (!f) {
225                 condlog(0,"can't fdopen wwids file : %s", strerror(errno));
226                 close(fd);
227                 return -1;
228         }
229         found = lookup_wwid(f, wwid);
230         if (found) {
231                 ret = 0;
232                 goto out;
233         }
234         if (!write_wwid) {
235                 ret = -1;
236                 goto out;
237         }
238         if (!can_write) {
239                 condlog(0, "wwids file is read-only. Can't write wwid");
240                 ret = -1;
241                 goto out;
242         }
243
244         if (fflush(f) != 0) {
245                 condlog(0, "cannot fflush wwids file stream : %s",
246                         strerror(errno));
247                 ret = -1;
248                 goto out;
249         }
250
251         ret = write_out_wwid(fd, wwid);
252 out:
253         fclose(f);
254         return ret;
255 }
256
257 int
258 remember_wwid(char *wwid)
259 {
260         int ret = check_wwids_file(wwid, 1);
261         if (ret < 0){
262                 condlog(3, "failed writing wwid %s to wwids file", wwid);
263                 return -1;
264         }
265         if (ret == 1)
266                 condlog(3, "wrote wwid %s to wwids file", wwid);
267         else
268                 condlog(4, "wwid %s already in wwids file", wwid);
269         return 0;
270 }