Initial Linux ZFS GIT Repo
[zfs.git] / zfs / lib / libdmu-ctl / dctl_client.c
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <ftw.h>
31 #include <errno.h>
32 #include <unistd.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/un.h>
36 #include <sys/debug.h>
37
38 #include <sys/dmu_ctl.h>
39 #include <sys/dmu_ctl_impl.h>
40
41 /*
42  * Try to connect to the socket given in path.
43  *
44  * For nftw() convenience, returns 0 if unsuccessful, otherwise
45  * returns the socket descriptor.
46  */
47 static int try_connect(const char *path)
48 {
49         struct sockaddr_un name;
50         int sock;
51
52         sock = socket(PF_UNIX, SOCK_STREAM, 0);
53         if (sock == -1) {
54                 perror("socket");
55                 return 0;
56         }
57
58         /*
59          * The socket fd cannot be 0 otherwise nftw() will not interpret the
60          * return code correctly.
61          */
62         VERIFY(sock != 0);
63
64         name.sun_family = AF_UNIX;
65         strncpy(name.sun_path, path, sizeof(name.sun_path));
66
67         name.sun_path[sizeof(name.sun_path) - 1] = '\0';
68
69         if (connect(sock, (struct sockaddr *) &name, sizeof(name)) == -1) {
70                 close(sock);
71                 return 0;
72         }
73
74         return sock;
75 }
76
77 /*
78  * nftw() callback.
79  */
80 static int nftw_cb(const char *fpath, const struct stat *sb, int typeflag,
81     struct FTW *ftwbuf)
82 {
83         if (!S_ISSOCK(sb->st_mode))
84                 return 0;
85
86         if (strcmp(&fpath[ftwbuf->base], SOCKNAME) != 0)
87                 return 0;
88
89         return try_connect(fpath);
90 }
91
92 /*
93  * For convenience, if check_subdirs is true we walk the directory tree to
94  * find a good socket.
95  */
96 int dctlc_connect(const char *dir, boolean_t check_subdirs)
97 {
98         char *fpath;
99         int fd;
100
101         if (check_subdirs)
102                 fd = nftw(dir, nftw_cb, 10, FTW_PHYS);
103         else {
104                 fpath = malloc(strlen(dir) + strlen(SOCKNAME) + 2);
105                 if (fpath == NULL)
106                         return -1;
107
108                 strcpy(fpath, dir);
109                 strcat(fpath, "/" SOCKNAME);
110
111                 fd = try_connect(fpath);
112
113                 free(fpath);
114         }
115
116         return fd == 0 ? -1 : fd;
117 }
118
119 void dctlc_disconnect(int fd)
120 {
121         (void) shutdown(fd, SHUT_RDWR);
122 }
123
124 static int dctl_reply_copyin(int fd, dctl_cmd_t *cmd)
125 {
126         return dctl_send_data(fd, (void *)(uintptr_t) cmd->u.dcmd_copy.ptr,
127             cmd->u.dcmd_copy.size);
128 }
129
130 static int dctl_reply_copyinstr(int fd, dctl_cmd_t *cmd)
131 {
132         dctl_cmd_t reply;
133         char *from;
134         size_t len, buflen, to_copy;
135         int error;
136
137         reply.dcmd_msg = DCTL_GEN_REPLY;
138
139         from = (char *)(uintptr_t) cmd->u.dcmd_copy.ptr;
140
141         buflen = cmd->u.dcmd_copy.size;
142         to_copy = strnlen(from, buflen - 1);
143
144         reply.u.dcmd_reply.rc = from[to_copy] == '\0' ? 0 : ENAMETOOLONG;
145         reply.u.dcmd_reply.size = to_copy;
146
147         error = dctl_send_msg(fd, &reply);
148
149         if (!error && to_copy > 0)
150                 error = dctl_send_data(fd, from, to_copy);
151
152         return error;
153 }
154
155 static int dctl_reply_copyout(int fd, dctl_cmd_t *cmd)
156 {
157         return dctl_read_data(fd, (void *)(uintptr_t) cmd->u.dcmd_copy.ptr,
158             cmd->u.dcmd_copy.size);
159 }
160
161 static int dctl_reply_fd_read(int fd, dctl_cmd_t *cmd)
162 {
163         dctl_cmd_t reply;
164         void *buf;
165         int error;
166         ssize_t rrc, size = cmd->u.dcmd_fd_io.size;
167
168         buf = malloc(size);
169         if (buf == NULL)
170                 return ENOMEM;
171
172         rrc = read(cmd->u.dcmd_fd_io.fd, buf, size);
173
174         reply.dcmd_msg = DCTL_GEN_REPLY;
175         reply.u.dcmd_reply.rc = rrc == -1 ? errno : 0;
176         reply.u.dcmd_reply.size = rrc;
177
178         error = dctl_send_msg(fd, &reply);
179
180         if (!error && rrc > 0)
181                 error = dctl_send_data(fd, buf, rrc);
182
183 out:
184         free(buf);
185
186         return error;
187 }
188
189 static int dctl_reply_fd_write(int fd, dctl_cmd_t *cmd)
190 {
191         dctl_cmd_t reply;
192         void *buf;
193         int error;
194         ssize_t wrc, size = cmd->u.dcmd_fd_io.size;
195
196         buf = malloc(size);
197         if (buf == NULL)
198                 return ENOMEM;
199
200         error = dctl_read_data(fd, buf, size);
201         if (error)
202                 goto out;
203
204         wrc = write(cmd->u.dcmd_fd_io.fd, buf, size);
205
206         reply.dcmd_msg = DCTL_GEN_REPLY;
207         reply.u.dcmd_reply.rc = wrc == -1 ? errno : 0;
208         reply.u.dcmd_reply.size = wrc;
209
210         error = dctl_send_msg(fd, &reply);
211
212 out:
213         free(buf);
214
215         return error;
216 }
217
218 int dctlc_ioctl(int fd, int32_t request, void *arg)
219 {
220         int error;
221         dctl_cmd_t cmd;
222
223         ASSERT(fd != 0);
224
225         cmd.dcmd_msg = DCTL_IOCTL;
226
227         cmd.u.dcmd_ioctl.cmd = request;
228         cmd.u.dcmd_ioctl.arg = (uintptr_t) arg;
229
230         error = dctl_send_msg(fd, &cmd);
231
232         while (!error && (error = dctl_read_msg(fd, &cmd)) == 0) {
233                 switch (cmd.dcmd_msg) {
234                         case DCTL_IOCTL_REPLY:
235                                 error = cmd.u.dcmd_reply.rc;
236                                 goto out;
237                         case DCTL_COPYIN:
238                                 error = dctl_reply_copyin(fd, &cmd);
239                                 break;
240                         case DCTL_COPYINSTR:
241                                 error = dctl_reply_copyinstr(fd, &cmd);
242                                 break;
243                         case DCTL_COPYOUT:
244                                 error = dctl_reply_copyout(fd, &cmd);
245                                 break;
246                         case DCTL_FD_READ:
247                                 error = dctl_reply_fd_read(fd, &cmd);
248                                 break;
249                         case DCTL_FD_WRITE:
250                                 error = dctl_reply_fd_write(fd, &cmd);
251                                 break;
252                         default:
253                                 fprintf(stderr, "%s(): invalid message "
254                                     "received.\n", __func__);
255                                 error = EINVAL;
256                                 goto out;
257                 }
258         }
259
260 out:
261         errno = error;
262         return error ? -1 : 0;
263 }