1 | /* |
2 | * SCM_RIGHTS with unix socket help program for test |
3 | * |
4 | * Copyright IBM, Inc. 2013 |
5 | * |
6 | * Authors: |
7 | * Wenchao Xia <xiawenc@linux.vnet.ibm.com> |
8 | * |
9 | * This work is licensed under the terms of the GNU LGPL, version 2 or later. |
10 | * See the COPYING.LIB file in the top-level directory. |
11 | */ |
12 | |
13 | #include "qemu/osdep.h" |
14 | #include <sys/socket.h> |
15 | #include <sys/un.h> |
16 | |
17 | /* #define SOCKET_SCM_DEBUG */ |
18 | |
19 | /* |
20 | * @fd and @fd_to_send will not be checked for validation in this function, |
21 | * a blank will be sent as iov data to notify qemu. |
22 | */ |
23 | static int send_fd(int fd, int fd_to_send) |
24 | { |
25 | struct msghdr msg; |
26 | struct iovec iov[1]; |
27 | int ret; |
28 | char control[CMSG_SPACE(sizeof(int))]; |
29 | struct cmsghdr *cmsg; |
30 | |
31 | memset(&msg, 0, sizeof(msg)); |
32 | memset(control, 0, sizeof(control)); |
33 | |
34 | /* Send a blank to notify qemu */ |
35 | iov[0].iov_base = (void *)" " ; |
36 | iov[0].iov_len = 1; |
37 | |
38 | msg.msg_iov = iov; |
39 | msg.msg_iovlen = 1; |
40 | |
41 | msg.msg_control = control; |
42 | msg.msg_controllen = sizeof(control); |
43 | |
44 | cmsg = CMSG_FIRSTHDR(&msg); |
45 | |
46 | cmsg->cmsg_len = CMSG_LEN(sizeof(int)); |
47 | cmsg->cmsg_level = SOL_SOCKET; |
48 | cmsg->cmsg_type = SCM_RIGHTS; |
49 | memcpy(CMSG_DATA(cmsg), &fd_to_send, sizeof(int)); |
50 | |
51 | do { |
52 | ret = sendmsg(fd, &msg, 0); |
53 | } while (ret < 0 && errno == EINTR); |
54 | |
55 | if (ret < 0) { |
56 | fprintf(stderr, "Failed to send msg, reason: %s\n" , strerror(errno)); |
57 | } |
58 | |
59 | return ret; |
60 | } |
61 | |
62 | /* Convert string to fd number. */ |
63 | static int get_fd_num(const char *fd_str, bool silent) |
64 | { |
65 | int sock; |
66 | char *err; |
67 | |
68 | errno = 0; |
69 | sock = strtol(fd_str, &err, 10); |
70 | if (errno) { |
71 | if (!silent) { |
72 | fprintf(stderr, "Failed in strtol for socket fd, reason: %s\n" , |
73 | strerror(errno)); |
74 | } |
75 | return -1; |
76 | } |
77 | if (!*fd_str || *err || sock < 0) { |
78 | if (!silent) { |
79 | fprintf(stderr, "bad numerical value for socket fd '%s'\n" , fd_str); |
80 | } |
81 | return -1; |
82 | } |
83 | |
84 | return sock; |
85 | } |
86 | |
87 | /* |
88 | * To make things simple, the caller needs to specify: |
89 | * 1. socket fd. |
90 | * 2. path of the file to be sent. |
91 | */ |
92 | int main(int argc, char **argv, char **envp) |
93 | { |
94 | int sock, fd, ret; |
95 | |
96 | #ifdef SOCKET_SCM_DEBUG |
97 | int i; |
98 | for (i = 0; i < argc; i++) { |
99 | fprintf(stderr, "Parameter %d: %s\n" , i, argv[i]); |
100 | } |
101 | #endif |
102 | |
103 | if (argc != 3) { |
104 | fprintf(stderr, |
105 | "Usage: %s < socket-fd > < file-path >\n" , |
106 | argv[0]); |
107 | return EXIT_FAILURE; |
108 | } |
109 | |
110 | |
111 | sock = get_fd_num(argv[1], false); |
112 | if (sock < 0) { |
113 | return EXIT_FAILURE; |
114 | } |
115 | |
116 | fd = get_fd_num(argv[2], true); |
117 | if (fd < 0) { |
118 | /* Now only open a file in readonly mode for test purpose. If more |
119 | precise control is needed, use python script in file operation, which |
120 | is supposed to fork and exec this program. */ |
121 | fd = open(argv[2], O_RDONLY); |
122 | if (fd < 0) { |
123 | fprintf(stderr, "Failed to open file '%s'\n" , argv[2]); |
124 | return EXIT_FAILURE; |
125 | } |
126 | } |
127 | |
128 | ret = send_fd(sock, fd); |
129 | if (ret < 0) { |
130 | close(fd); |
131 | return EXIT_FAILURE; |
132 | } |
133 | |
134 | close(fd); |
135 | return EXIT_SUCCESS; |
136 | } |
137 | |