multipath-tools: replace <> with "" for local headers
[multipath-tools/.git] / libmultipath / prioritizers / iet.c
1 #include <dirent.h>
2 #include <stdlib.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <stdio.h>
6 #include <string.h>
7 #include <regex.h>
8 #include "prio.h"
9 #include "debug.h"
10 #include <unistd.h>
11 #include "structs.h"
12
13 //
14 // This prioritizer suits iSCSI needs, makes it possible to prefer one path.
15 //
16 // (It's a bit of a misnomer since supports the client side [eg. open-iscsi]
17 //  instead of just "iet".)
18 //
19 // Usage:
20 //   prio      "iet"
21 //   prio_args "preferredip=10.11.12.13"
22 //
23 // Uses /dev/disk/by-path to find the IP of the device.
24 // Assigns prio 20 (high) to the preferred IP and prio 10 (low) to the rest.
25 //
26 // by Olivier Lambert <lambert.olivier.gmail.com>
27 //
28
29 #define dc_log(prio, msg) condlog(prio, "%s: iet prio: " msg, dev)
30 //
31 // name: find_regex
32 // @param string: string you want to search into
33 // @param regex: the pattern used
34 // @return result: string finded in string with regex, "none" if none
35 char *find_regex(char * string, char * regex)
36 {
37         int err;
38         regex_t preg;
39         err = regcomp(&preg, regex, REG_EXTENDED);
40
41         if (err == 0) {
42                 int match;
43                 size_t nmatch = 0;
44                 regmatch_t *pmatch = NULL;
45                 nmatch = preg.re_nsub;
46                 pmatch = malloc(sizeof(*pmatch) * nmatch);
47
48                 if (pmatch) {
49                         match = regexec(&preg, string, nmatch, pmatch, 0);
50                         regfree(&preg);
51
52                         if (match == 0) {
53                                 char *result = NULL;
54                                 int start = pmatch[0].rm_so;
55                                 int end = pmatch[0].rm_eo;
56                                 size_t size = end - start;
57                                 result = malloc (sizeof(*result) * (size + 1));
58
59                                 if (result) {
60                                         strncpy(result, &string[start], size);
61                                         result[size] = '\0';
62                                         free(pmatch);
63                                         return result;
64                                 }
65                         }
66                         free(pmatch);
67                 }
68         }
69         return NULL;
70 }
71
72 //
73 // name: inet_prio
74 // @param
75 // @return prio
76 int iet_prio(const char *dev, char * args)
77 {
78         char preferredip_buff[255] = "";
79         char *preferredip = &preferredip_buff[0];
80         // Phase 1 : checks. If anyone fails, return prio 0.
81         // check if args exists
82         if (!args) {
83                 dc_log(0, "need prio_args with preferredip set");
84                 return 0;
85         }
86         // check if args format is OK
87         if (sscanf(args, "preferredip=%s", preferredip) ==1) {}
88         else {
89                 dc_log(0, "unexpected prio_args format");
90                 return 0;
91         }
92         // check if ip is not too short
93         if (strlen(preferredip) <= 7) {
94                 dc_log(0, "prio args: preferredip too short");
95                 return 0;
96         }
97         // Phase 2 : find device in /dev/disk/by-path to match device/ip
98         DIR           *dir_p;
99         struct dirent *dir_entry_p;
100         enum { BUFFERSIZE = 1024 };
101         char buffer[BUFFERSIZE];
102         char fullpath[BUFFERSIZE] = "/dev/disk/by-path/";
103         dir_p = opendir(fullpath);
104
105         // loop to find device in /dev/disk/by-path
106         while( NULL != (dir_entry_p = readdir(dir_p))) {
107                 if (dir_entry_p->d_name[0] != '.') {
108                         char path[BUFFERSIZE] = "/dev/disk/by-path/";
109                         strcat(path,dir_entry_p->d_name);
110                         ssize_t nchars = readlink(path, buffer, sizeof(buffer)-1);
111                         if (nchars != -1) {
112                                 char *device;
113                                 buffer[nchars] = '\0';
114                                 device = find_regex(buffer,"(sd[a-z]+)");
115                                 // if device parsed is the right one
116                                 if (device!=NULL && strncmp(device, dev, strlen(device)) == 0) {
117                                         char *ip;
118                                         ip = find_regex(dir_entry_p->d_name,"([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})");
119                                         // if prefferedip and ip fetched matches
120                                         if (ip!=NULL && strncmp(ip, preferredip, strlen(ip)) == 0) {
121                                                 // high prio
122                                                 free(ip);
123                                                 free(device);
124                                                 closedir(dir_p);
125                                                 return 20;
126                                         }
127                                         free(ip);
128                                 }
129                                 free(device);
130                         }
131                         else {
132                                 printf("error\n");
133                         }
134                 }
135         }
136         // nothing found, low prio
137         closedir(dir_p);
138         return 10;
139 }
140
141 int getprio(struct path * pp, char * args)
142 {
143         return iet_prio(pp->dev, args);
144 }