aboutsummaryrefslogtreecommitdiff
path: root/tests/qemu-iotests/socket_scm_helper.c
blob: eb76d31aa9486ebdd3cfc58a40b052978b9ab67b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
 * SCM_RIGHTS with unix socket help program for test
 *
 * Copyright IBM, Inc. 2013
 *
 * Authors:
 *  Wenchao Xia    <xiawenc@linux.vnet.ibm.com>
 *
 * This work is licensed under the terms of the GNU LGPL, version 2 or later.
 * See the COPYING.LIB file in the top-level directory.
 */

#include "qemu/osdep.h"
#include <sys/socket.h>
#include <sys/un.h>

/* #define SOCKET_SCM_DEBUG */

/*
 * @fd and @fd_to_send will not be checked for validation in this function,
 * a blank will be sent as iov data to notify qemu.
 */
static int send_fd(int fd, int fd_to_send)
{
    struct msghdr msg;
    struct iovec iov[1];
    int ret;
    char control[CMSG_SPACE(sizeof(int))];
    struct cmsghdr *cmsg;

    memset(&msg, 0, sizeof(msg));
    memset(control, 0, sizeof(control));

    /* Send a blank to notify qemu */
    iov[0].iov_base = (void *)" ";
    iov[0].iov_len = 1;

    msg.msg_iov = iov;
    msg.msg_iovlen = 1;

    msg.msg_control = control;
    msg.msg_controllen = sizeof(control);

    cmsg = CMSG_FIRSTHDR(&msg);

    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
    cmsg->cmsg_level = SOL_SOCKET;
    cmsg->cmsg_type = SCM_RIGHTS;
    memcpy(CMSG_DATA(cmsg), &fd_to_send, sizeof(int));

    do {
        ret = sendmsg(fd, &msg, 0);
    } while (ret < 0 && errno == EINTR);

    if (ret < 0) {
        fprintf(stderr, "Failed to send msg, reason: %s\n", strerror(errno));
    }

    return ret;
}

/* Convert string to fd number. */
static int get_fd_num(const char *fd_str, bool silent)
{
    int sock;
    char *err;

    errno = 0;
    sock = strtol(fd_str, &err, 10);
    if (errno) {
        if (!silent) {
            fprintf(stderr, "Failed in strtol for socket fd, reason: %s\n",
                    strerror(errno));
        }
        return -1;
    }
    if (!*fd_str || *err || sock < 0) {
        if (!silent) {
            fprintf(stderr, "bad numerical value for socket fd '%s'\n", fd_str);
        }
        return -1;
    }

    return sock;
}

/*
 * To make things simple, the caller needs to specify:
 * 1. socket fd.
 * 2. path of the file to be sent.
 */
int main(int argc, char **argv, char **envp)
{
    int sock, fd, ret;

#ifdef SOCKET_SCM_DEBUG
    int i;
    for (i = 0; i < argc; i++) {
        fprintf(stderr, "Parameter %d: %s\n", i, argv[i]);
    }
#endif

    if (argc != 3) {
        fprintf(stderr,
                "Usage: %s < socket-fd > < file-path >\n",
                argv[0]);
        return EXIT_FAILURE;
    }


    sock = get_fd_num(argv[1], false);
    if (sock < 0) {
        return EXIT_FAILURE;
    }

    fd = get_fd_num(argv[2], true);
    if (fd < 0) {
        /* Now only open a file in readonly mode for test purpose. If more
           precise control is needed, use python script in file operation, which
           is supposed to fork and exec this program. */
        fd = open(argv[2], O_RDONLY);
        if (fd < 0) {
            fprintf(stderr, "Failed to open file '%s'\n", argv[2]);
            return EXIT_FAILURE;
        }
    }

    ret = send_fd(sock, fd);
    if (ret < 0) {
        close(fd);
        return EXIT_FAILURE;
    }

    close(fd);
    return EXIT_SUCCESS;
}