Add libmpathcmd library and use it internally
[multipath-tools/.git] / libmpathcmd / mpath_cmd.c
1 #include <stdlib.h>
2 #include <unistd.h>
3 #include <stdio.h>
4 #include <sys/types.h>
5 #include <sys/socket.h>
6 #include <sys/un.h>
7 #include <poll.h>
8 #include <string.h>
9 #include <errno.h>
10
11 #include "mpath_cmd.h"
12
13 /*
14  * keep reading until its all read
15  */
16 static ssize_t read_all(int fd, void *buf, size_t len, unsigned int timeout)
17 {
18         size_t total = 0;
19         ssize_t n;
20         int ret;
21         struct pollfd pfd;
22
23         while (len) {
24                 pfd.fd = fd;
25                 pfd.events = POLLIN;
26                 ret = poll(&pfd, 1, timeout);
27                 if (!ret) {
28                         errno = ETIMEDOUT;
29                         return -1;
30                 } else if (ret < 0) {
31                         if (errno == EINTR)
32                                 continue;
33                         return -1;
34                 } else if (!pfd.revents & POLLIN)
35                         continue;
36                 n = read(fd, buf, len);
37                 if (n < 0) {
38                         if ((errno == EINTR) || (errno == EAGAIN))
39                                 continue;
40                         return -1;
41                 }
42                 if (!n)
43                         return total;
44                 buf = n + (char *)buf;
45                 len -= n;
46                 total += n;
47         }
48         return total;
49 }
50
51 /*
52  * keep writing until it's all sent
53  */
54 static size_t write_all(int fd, const void *buf, size_t len)
55 {
56         size_t total = 0;
57
58         while (len) {
59                 ssize_t n = write(fd, buf, len);
60                 if (n < 0) {
61                         if ((errno == EINTR) || (errno == EAGAIN))
62                                 continue;
63                         return total;
64                 }
65                 if (!n)
66                         return total;
67                 buf = n + (char *)buf;
68                 len -= n;
69                 total += n;
70         }
71         return total;
72 }
73
74 /*
75  * connect to a unix domain socket
76  */
77 int mpath_connect(void)
78 {
79         int fd, len;
80         struct sockaddr_un addr;
81
82         memset(&addr, 0, sizeof(addr));
83         addr.sun_family = AF_LOCAL;
84         addr.sun_path[0] = '\0';
85         len = strlen(DEFAULT_SOCKET) + 1 + sizeof(sa_family_t);
86         strncpy(&addr.sun_path[1], DEFAULT_SOCKET, len);
87
88         fd = socket(AF_LOCAL, SOCK_STREAM, 0);
89         if (fd == -1)
90                 return -1;
91
92         if (connect(fd, (struct sockaddr *)&addr, len) == -1) {
93                 close(fd);
94                 return -1;
95         }
96
97         return fd;
98 }
99
100 int mpath_disconnect(int fd)
101 {
102         return close(fd);
103 }
104
105 ssize_t mpath_recv_reply_len(int fd, unsigned int timeout)
106 {
107         size_t len;
108         ssize_t ret;
109
110         ret = read_all(fd, &len, sizeof(len), timeout);
111         if (ret < 0)
112                 return ret;
113         if (ret != sizeof(len)) {
114                 errno = EIO;
115                 return ret;
116         }
117         return len;
118 }
119
120 int mpath_recv_reply_data(int fd, char *reply, size_t len,
121                           unsigned int timeout)
122 {
123         ssize_t ret;
124
125         ret = read_all(fd, reply, len, timeout);
126         if (ret < 0)
127                 return ret;
128         if (ret != len) {
129                 errno = EIO;
130                 return -1;
131         }
132         reply[len - 1] = '\0';
133         return 0;
134 }
135
136 int mpath_recv_reply(int fd, char **reply, unsigned int timeout)
137 {
138         int err;
139         ssize_t len;
140
141         *reply = NULL;
142         len = mpath_recv_reply_len(fd, timeout);
143         if (len <= 0)
144                 return len;
145         *reply = malloc(len);
146         if (!*reply)
147                 return -1;
148         err = mpath_recv_reply_data(fd, *reply, len, timeout);
149         if (err) {
150                 free(*reply);
151                 *reply = NULL;
152                 return err;
153         }
154         return 0;
155 }
156
157 int mpath_send_cmd(int fd, const char *cmd)
158 {
159         size_t len;
160
161         if (cmd != NULL)
162                 len = strlen(cmd) + 1;
163         else
164                 len = 0;
165         if (write_all(fd, &len, sizeof(len)) != sizeof(len))
166                 return -1;
167         if (len && write_all(fd, cmd, len) != len)
168                 return -1;
169         return 0;
170 }
171
172 int mpath_process_cmd(int fd, const char *cmd, char **reply,
173                       unsigned int timeout)
174 {
175         if (mpath_send_cmd(fd, cmd) != 0)
176                 return -1;
177         return mpath_recv_reply(fd, reply, timeout);
178 }