]>
Commit | Line | Data |
---|---|---|
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 | } |