debuggers.hg

view tools/blktap2/drivers/tapdisk-control.c @ 22848:6341fe0f4e5a

Added tag 4.1.0-rc2 for changeset 9dca60d88c63
author Keir Fraser <keir@xen.org>
date Tue Jan 25 14:06:55 2011 +0000 (2011-01-25)
parents 63d0f5348af2
children
line source
1 /*
2 * Copyright (c) 2008, XenSource Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of XenSource Inc. nor the names of its contributors
13 * may be used to endorse or promote products derived from this software
14 * without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
20 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28 #include <stdio.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <signal.h>
35 #include <sys/un.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
38 #include <sys/ioctl.h>
39 #include <sys/socket.h>
41 #include "list.h"
42 #include "tapdisk.h"
43 #include "blktap2.h"
44 #include "blktaplib.h"
45 #include "tapdisk-vbd.h"
46 #include "tapdisk-utils.h"
47 #include "tapdisk-server.h"
48 #include "tapdisk-message.h"
49 #include "tapdisk-disktype.h"
51 struct tapdisk_control {
52 char *path;
53 int socket;
54 int event_id;
55 };
57 struct tapdisk_control_connection {
58 int socket;
59 event_id_t event_id;
60 };
62 static struct tapdisk_control td_control;
64 static void
65 tapdisk_control_initialize(void)
66 {
67 td_control.socket = -1;
68 td_control.event_id = -1;
70 signal(SIGPIPE, SIG_IGN);
71 }
73 void
74 tapdisk_control_close(void)
75 {
76 if (td_control.path) {
77 unlink(td_control.path);
78 free(td_control.path);
79 td_control.path = NULL;
80 }
82 if (td_control.socket != -1) {
83 close(td_control.socket);
84 td_control.socket = -1;
85 }
86 }
88 static struct tapdisk_control_connection *
89 tapdisk_control_allocate_connection(int fd)
90 {
91 struct tapdisk_control_connection *connection;
92 size_t sz;
94 connection = calloc(1, sizeof(*connection));
95 if (!connection) {
96 EPRINTF("calloc");
97 return NULL;
98 }
100 connection->socket = fd;
101 return connection;
102 }
104 static void
105 tapdisk_control_close_connection(struct tapdisk_control_connection *connection)
106 {
107 tapdisk_server_unregister_event(connection->event_id);
108 close(connection->socket);
109 free(connection);
110 }
112 static int
113 tapdisk_control_read_message(int fd, tapdisk_message_t *message, int timeout)
114 {
115 fd_set readfds;
116 int ret, len, offset;
117 struct timeval tv, *t;
119 t = NULL;
120 offset = 0;
121 len = sizeof(tapdisk_message_t);
123 if (timeout) {
124 tv.tv_sec = timeout;
125 tv.tv_usec = 0;
126 t = &tv;
127 }
129 memset(message, 0, sizeof(tapdisk_message_t));
131 while (offset < len) {
132 FD_ZERO(&readfds);
133 FD_SET(fd, &readfds);
135 ret = select(fd + 1, &readfds, NULL, NULL, t);
136 if (ret == -1)
137 break;
138 else if (FD_ISSET(fd, &readfds)) {
139 ret = read(fd, message + offset, len - offset);
140 if (ret <= 0)
141 break;
142 offset += ret;
143 } else
144 break;
145 }
147 if (offset != len) {
148 EPRINTF("failure reading message (wanted %d but got %d)\n",
149 len, offset);
150 return -EIO;
151 }
153 DPRINTF("received '%s' message (uuid = %u)\n",
154 tapdisk_message_name(message->type), message->cookie);
156 return 0;
157 }
159 static int
160 tapdisk_control_write_message(int fd, tapdisk_message_t *message, int timeout)
161 {
162 fd_set writefds;
163 int ret, len, offset;
164 struct timeval tv, *t;
166 t = NULL;
167 offset = 0;
168 len = sizeof(tapdisk_message_t);
170 if (timeout) {
171 tv.tv_sec = timeout;
172 tv.tv_usec = 0;
173 t = &tv;
174 }
176 DPRINTF("sending '%s' message (uuid = %u)\n",
177 tapdisk_message_name(message->type), message->cookie);
179 while (offset < len) {
180 FD_ZERO(&writefds);
181 FD_SET(fd, &writefds);
183 /* we don't bother reinitializing tv. at worst, it will wait a
184 * bit more time than expected. */
186 ret = select(fd + 1, NULL, &writefds, NULL, t);
187 if (ret == -1)
188 break;
189 else if (FD_ISSET(fd, &writefds)) {
190 ret = write(fd, message + offset, len - offset);
191 if (ret <= 0)
192 break;
193 offset += ret;
194 } else
195 break;
196 }
198 if (offset != len) {
199 EPRINTF("failure writing message\n");
200 return -EIO;
201 }
203 return 0;
204 }
206 static int
207 tapdisk_control_validate_request(tapdisk_message_t *request)
208 {
209 if (strnlen(request->u.params.path,
210 TAPDISK_MESSAGE_MAX_PATH_LENGTH) >=
211 TAPDISK_MESSAGE_MAX_PATH_LENGTH)
212 return EINVAL;
214 return 0;
215 }
217 static void
218 tapdisk_control_list_minors(struct tapdisk_control_connection *connection,
219 tapdisk_message_t *request)
220 {
221 int i;
222 td_vbd_t *vbd;
223 struct list_head *head;
224 tapdisk_message_t response;
226 i = 0;
227 memset(&response, 0, sizeof(response));
229 response.type = TAPDISK_MESSAGE_LIST_MINORS_RSP;
230 response.cookie = request->cookie;
232 head = tapdisk_server_get_all_vbds();
234 list_for_each_entry(vbd, head, next) {
235 response.u.minors.list[i++] = vbd->minor;
236 if (i >= TAPDISK_MESSAGE_MAX_MINORS) {
237 response.type = TAPDISK_MESSAGE_ERROR;
238 response.u.response.error = ERANGE;
239 break;
240 }
241 }
243 response.u.minors.count = i;
244 tapdisk_control_write_message(connection->socket, &response, 2);
245 tapdisk_control_close_connection(connection);
246 }
248 static void
249 tapdisk_control_list(struct tapdisk_control_connection *connection,
250 tapdisk_message_t *request)
251 {
252 td_vbd_t *vbd;
253 struct list_head *head;
254 tapdisk_message_t response;
255 int count, i;
257 memset(&response, 0, sizeof(response));
258 response.type = TAPDISK_MESSAGE_LIST_RSP;
259 response.cookie = request->cookie;
261 head = tapdisk_server_get_all_vbds();
263 count = 0;
264 list_for_each_entry(vbd, head, next)
265 count++;
267 list_for_each_entry(vbd, head, next) {
268 response.u.list.count = count--;
269 response.u.list.minor = vbd->minor;
270 response.u.list.state = vbd->state;
271 response.u.list.path[0] = 0;
273 if (!list_empty(&vbd->images)) {
274 td_image_t *image = list_entry(vbd->images.next,
275 td_image_t, next);
276 snprintf(response.u.list.path,
277 sizeof(response.u.list.path),
278 "%s:%s",
279 tapdisk_disk_types[image->type]->name,
280 image->name);
281 }
283 tapdisk_control_write_message(connection->socket, &response, 2);
284 }
286 response.u.list.count = count;
287 response.u.list.minor = -1;
288 response.u.list.path[0] = 0;
290 tapdisk_control_write_message(connection->socket, &response, 2);
291 tapdisk_control_close_connection(connection);
292 }
294 static void
295 tapdisk_control_get_pid(struct tapdisk_control_connection *connection,
296 tapdisk_message_t *request)
297 {
298 tapdisk_message_t response;
300 memset(&response, 0, sizeof(response));
301 response.type = TAPDISK_MESSAGE_PID_RSP;
302 response.cookie = request->cookie;
303 response.u.tapdisk_pid = getpid();
305 tapdisk_control_write_message(connection->socket, &response, 2);
306 tapdisk_control_close_connection(connection);
307 }
309 static void
310 tapdisk_control_attach_vbd(struct tapdisk_control_connection *connection,
311 tapdisk_message_t *request)
312 {
313 tapdisk_message_t response;
314 char *devname;
315 td_vbd_t *vbd;
316 struct blktap2_params params;
317 image_t image;
318 int minor, err;
320 /*
321 * TODO: check for max vbds per process
322 */
324 vbd = tapdisk_server_get_vbd(request->cookie);
325 if (vbd) {
326 err = -EEXIST;
327 goto out;
328 }
330 minor = request->cookie;
331 if (minor < 0) {
332 err = -EINVAL;
333 goto out;
334 }
336 vbd = tapdisk_vbd_create(minor);
337 if (!vbd) {
338 err = -ENOMEM;
339 goto out;
340 }
342 err = asprintf(&devname, BLKTAP2_RING_DEVICE"%d", minor);
343 if (err == -1) {
344 err = -ENOMEM;
345 goto fail_vbd;
346 }
348 err = tapdisk_vbd_attach(vbd, devname, minor);
349 free(devname);
350 if (err)
351 goto fail_vbd;
353 tapdisk_server_add_vbd(vbd);
355 out:
356 memset(&response, 0, sizeof(response));
357 response.type = TAPDISK_MESSAGE_ATTACH_RSP;
358 response.cookie = request->cookie;
359 response.u.response.error = -err;
361 tapdisk_control_write_message(connection->socket, &response, 2);
362 tapdisk_control_close_connection(connection);
364 return;
366 fail_vbd:
367 tapdisk_vbd_detach(vbd);
368 free(vbd);
369 goto out;
370 }
373 static void
374 tapdisk_control_detach_vbd(struct tapdisk_control_connection *connection,
375 tapdisk_message_t *request)
376 {
377 tapdisk_message_t response;
378 td_vbd_t *vbd;
379 int err;
381 vbd = tapdisk_server_get_vbd(request->cookie);
382 if (!vbd) {
383 err = -EINVAL;
384 goto out;
385 }
387 tapdisk_vbd_detach(vbd);
389 if (list_empty(&vbd->images)) {
390 tapdisk_server_remove_vbd(vbd);
391 free(vbd);
392 }
394 err = 0;
395 out:
396 memset(&response, 0, sizeof(response));
397 response.type = TAPDISK_MESSAGE_DETACH_RSP;
398 response.cookie = request->cookie;
399 response.u.response.error = -err;
401 tapdisk_control_write_message(connection->socket, &response, 2);
402 tapdisk_control_close_connection(connection);
403 }
405 static void
406 tapdisk_control_open_image(struct tapdisk_control_connection *connection,
407 tapdisk_message_t *request)
408 {
409 int err;
410 image_t image;
411 td_vbd_t *vbd;
412 td_flag_t flags;
413 tapdisk_message_t response;
414 struct blktap2_params params;
416 vbd = tapdisk_server_get_vbd(request->cookie);
417 if (!vbd) {
418 err = -EINVAL;
419 goto out;
420 }
422 if (vbd->minor == -1) {
423 err = -EINVAL;
424 goto out;
425 }
427 if (vbd->name) {
428 err = -EALREADY;
429 goto out;
430 }
432 flags = 0;
433 if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_RDONLY)
434 flags |= TD_OPEN_RDONLY;
435 if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_SHARED)
436 flags |= TD_OPEN_SHAREABLE;
437 if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_ADD_CACHE)
438 flags |= TD_OPEN_ADD_CACHE;
439 if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_VHD_INDEX)
440 flags |= TD_OPEN_VHD_INDEX;
441 if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_LOG_DIRTY)
442 flags |= TD_OPEN_LOG_DIRTY;
444 vbd->name = strndup(request->u.params.path,
445 sizeof(request->u.params.path));
446 if (!vbd->name) {
447 err = -ENOMEM;
448 goto out;
449 }
451 err = tapdisk_vbd_parse_stack(vbd, request->u.params.path);
452 if (err)
453 goto out;
455 err = tapdisk_vbd_open_stack(vbd, request->u.params.storage, flags);
456 if (err)
457 goto out;
459 err = tapdisk_vbd_get_image_info(vbd, &image);
460 if (err)
461 goto fail_close;
463 params.capacity = image.size;
464 params.sector_size = image.secsize;
466 err = ioctl(vbd->ring.fd, BLKTAP2_IOCTL_CREATE_DEVICE, &params);
467 if (err && errno != EEXIST) {
468 err = -errno;
469 EPRINTF("create device failed: %d\n", err);
470 goto fail_close;
471 }
473 err = 0;
475 out:
476 memset(&response, 0, sizeof(response));
477 response.cookie = request->cookie;
479 if (err) {
480 response.type = TAPDISK_MESSAGE_ERROR;
481 response.u.response.error = -err;
482 } else {
483 response.u.image.sectors = image.size;
484 response.u.image.sector_size = image.secsize;
485 response.u.image.info = image.info;
486 response.type = TAPDISK_MESSAGE_OPEN_RSP;
487 }
489 tapdisk_control_write_message(connection->socket, &response, 2);
490 tapdisk_control_close_connection(connection);
492 return;
494 fail_close:
495 tapdisk_vbd_close_vdi(vbd);
496 free(vbd->name);
497 vbd->name = NULL;
498 goto out;
499 }
501 static void
502 tapdisk_control_close_image(struct tapdisk_control_connection *connection,
503 tapdisk_message_t *request)
504 {
505 tapdisk_message_t response;
506 td_vbd_t *vbd;
507 int err;
509 vbd = tapdisk_server_get_vbd(request->cookie);
510 if (!vbd) {
511 err = -EINVAL;
512 goto out;
513 }
515 if (!list_empty(&vbd->pending_requests)) {
516 err = -EAGAIN;
517 goto out;
518 }
520 tapdisk_vbd_close_vdi(vbd);
522 /* NB. vbd->name free should probably belong into close_vdi,
523 but the current blktap1 reopen-stuff likely depends on a
524 lifetime extended until shutdown. */
525 free(vbd->name);
526 vbd->name = NULL;
528 if (vbd->minor == -1) {
529 tapdisk_server_remove_vbd(vbd);
530 tapdisk_vbd_free(vbd);
531 }
533 err = 0;
534 out:
535 memset(&response, 0, sizeof(response));
536 response.type = TAPDISK_MESSAGE_CLOSE_RSP;
537 response.cookie = request->cookie;
538 response.u.response.error = -err;
540 tapdisk_control_write_message(connection->socket, &response, 2);
541 tapdisk_control_close_connection(connection);
542 }
544 static void
545 tapdisk_control_pause_vbd(struct tapdisk_control_connection *connection,
546 tapdisk_message_t *request)
547 {
548 int err;
549 td_vbd_t *vbd;
550 tapdisk_message_t response;
552 memset(&response, 0, sizeof(response));
554 response.type = TAPDISK_MESSAGE_PAUSE_RSP;
556 vbd = tapdisk_server_get_vbd(request->cookie);
557 if (!vbd) {
558 err = -EINVAL;
559 goto out;
560 }
562 do {
563 err = tapdisk_vbd_pause(vbd);
565 if (!err || err != -EAGAIN)
566 break;
568 tapdisk_server_iterate();
569 } while (1);
571 out:
572 response.cookie = request->cookie;
573 response.u.response.error = -err;
574 tapdisk_control_write_message(connection->socket, &response, 2);
575 tapdisk_control_close_connection(connection);
576 }
578 static void
579 tapdisk_control_resume_vbd(struct tapdisk_control_connection *connection,
580 tapdisk_message_t *request)
581 {
582 int err;
583 td_vbd_t *vbd;
584 tapdisk_message_t response;
586 memset(&response, 0, sizeof(response));
588 response.type = TAPDISK_MESSAGE_RESUME_RSP;
590 vbd = tapdisk_server_get_vbd(request->cookie);
591 if (!vbd) {
592 err = -EINVAL;
593 goto out;
594 }
596 if (!td_flag_test(vbd->state, TD_VBD_PAUSED)) {
597 err = -EINVAL;
598 goto out;
599 }
601 if (request->u.params.path[0]) {
602 free(vbd->name);
603 vbd->name = strndup(request->u.params.path,
604 sizeof(request->u.params.path));
605 if (!vbd->name) {
606 err = -ENOMEM;
607 goto out;
608 }
609 } else if (!vbd->name) {
610 err = -EINVAL;
611 goto out;
612 }
614 err = tapdisk_vbd_parse_stack(vbd, vbd->name);
615 if (err)
616 goto out;
618 err = tapdisk_vbd_resume(vbd, NULL, -1);
619 if (err)
620 goto out;
622 out:
623 response.cookie = request->cookie;
624 response.u.response.error = -err;
625 tapdisk_control_write_message(connection->socket, &response, 2);
626 tapdisk_control_close_connection(connection);
627 }
629 static void
630 tapdisk_control_handle_request(event_id_t id, char mode, void *private)
631 {
632 int err;
633 tapdisk_message_t message;
634 struct tapdisk_control_connection *connection =
635 (struct tapdisk_control_connection *)private;
637 if (tapdisk_control_read_message(connection->socket, &message, 2)) {
638 EPRINTF("failed to read message from %d\n", connection->socket);
639 tapdisk_control_close_connection(connection);
640 return;
641 }
643 err = tapdisk_control_validate_request(&message);
644 if (err)
645 goto fail;
647 switch (message.type) {
648 case TAPDISK_MESSAGE_PID:
649 return tapdisk_control_get_pid(connection, &message);
650 case TAPDISK_MESSAGE_LIST_MINORS:
651 return tapdisk_control_list_minors(connection, &message);
652 case TAPDISK_MESSAGE_LIST:
653 return tapdisk_control_list(connection, &message);
654 case TAPDISK_MESSAGE_ATTACH:
655 return tapdisk_control_attach_vbd(connection, &message);
656 case TAPDISK_MESSAGE_DETACH:
657 return tapdisk_control_detach_vbd(connection, &message);
658 case TAPDISK_MESSAGE_OPEN:
659 return tapdisk_control_open_image(connection, &message);
660 case TAPDISK_MESSAGE_PAUSE:
661 return tapdisk_control_pause_vbd(connection, &message);
662 case TAPDISK_MESSAGE_RESUME:
663 return tapdisk_control_resume_vbd(connection, &message);
664 case TAPDISK_MESSAGE_CLOSE:
665 return tapdisk_control_close_image(connection, &message);
666 default: {
667 tapdisk_message_t response;
668 fail:
670 EPRINTF("received unsupported message '%s'\n",
671 tapdisk_message_name(message.type));
673 memset(&response, 0, sizeof(response));
675 response.type = TAPDISK_MESSAGE_ERROR;
676 response.u.response.error = (err ? -err : EINVAL);
677 tapdisk_control_write_message(connection->socket, &response, 2);
679 tapdisk_control_close_connection(connection);
680 break;
681 }
682 }
683 }
685 static void
686 tapdisk_control_accept(event_id_t id, char mode, void *private)
687 {
688 int err, fd;
689 struct tapdisk_control_connection *connection;
691 fd = accept(td_control.socket, NULL, NULL);
692 if (fd == -1) {
693 EPRINTF("failed to accept new control connection: %d\n", errno);
694 return;
695 }
697 connection = tapdisk_control_allocate_connection(fd);
698 if (!connection) {
699 close(fd);
700 EPRINTF("failed to allocate new control connection\n");
701 }
703 err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
704 connection->socket, 0,
705 tapdisk_control_handle_request,
706 connection);
707 if (err == -1) {
708 close(fd);
709 free(connection);
710 EPRINTF("failed to register new control event: %d\n", err);
711 }
713 connection->event_id = err;
714 }
716 static int
717 tapdisk_control_mkdir(const char *dir)
718 {
719 int err;
720 char *ptr, *name, *start;
722 err = access(dir, W_OK | R_OK);
723 if (!err)
724 return 0;
726 name = strdup(dir);
727 if (!name)
728 return -ENOMEM;
730 start = name;
732 for (;;) {
733 ptr = strchr(start + 1, '/');
734 if (ptr)
735 *ptr = '\0';
737 err = mkdir(name, 0755);
738 if (err && errno != EEXIST) {
739 err = -errno;
740 EPRINTF("failed to create directory %s: %d\n",
741 name, err);
742 break;
743 }
745 if (!ptr)
746 break;
747 else {
748 *ptr = '/';
749 start = ptr + 1;
750 }
751 }
753 free(name);
754 return err;
755 }
757 static int
758 tapdisk_control_create_socket(char **socket_path)
759 {
760 int err, flags;
761 struct sockaddr_un saddr;
763 err = tapdisk_control_mkdir(BLKTAP2_CONTROL_DIR);
764 if (err) {
765 EPRINTF("failed to create directory %s: %d\n",
766 BLKTAP2_CONTROL_DIR, err);
767 return err;
768 }
770 err = asprintf(&td_control.path, "%s/%s%d",
771 BLKTAP2_CONTROL_DIR, BLKTAP2_CONTROL_SOCKET, getpid());
772 if (err == -1) {
773 td_control.path = NULL;
774 err = (errno ? : ENOMEM);
775 goto fail;
776 }
778 if (unlink(td_control.path) && errno != ENOENT) {
779 err = errno;
780 EPRINTF("failed to unlink %s: %d\n", td_control.path, errno);
781 goto fail;
782 }
784 td_control.socket = socket(AF_UNIX, SOCK_STREAM, 0);
785 if (td_control.socket == -1) {
786 err = errno;
787 EPRINTF("failed to create control socket: %d\n", err);
788 goto fail;
789 }
791 memset(&saddr, 0, sizeof(saddr));
792 strncpy(saddr.sun_path, td_control.path, sizeof(saddr.sun_path));
793 saddr.sun_family = AF_UNIX;
795 err = bind(td_control.socket,
796 (const struct sockaddr *)&saddr, sizeof(saddr));
797 if (err == -1) {
798 err = errno;
799 EPRINTF("failed to bind to %s: %d\n", saddr.sun_path, err);
800 goto fail;
801 }
803 err = listen(td_control.socket, 10);
804 if (err == -1) {
805 err = errno;
806 EPRINTF("failed to listen: %d\n", err);
807 goto fail;
808 }
810 err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
811 td_control.socket, 0,
812 tapdisk_control_accept, NULL);
813 if (err < 0) {
814 EPRINTF("failed to add watch: %d\n", err);
815 goto fail;
816 }
818 td_control.event_id = err;
819 *socket_path = td_control.path;
821 return 0;
823 fail:
824 tapdisk_control_close();
825 return err;
826 }
828 int
829 tapdisk_control_open(char **path)
830 {
831 int err;
833 tapdisk_control_initialize();
835 return tapdisk_control_create_socket(path);
836 }