766531eaa68f020505d6e684af46b3f8e81ecec0
[splice.git] / ktee-net.c
1 /*
2  * A tee implementation using sys_tee. Sends out the data received over
3  * stdin to the given host:port and over stdout.
4  */
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <string.h>
9 #include <fcntl.h>
10 #include <netinet/in.h>
11 #include <arpa/inet.h>
12 #include <netdb.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <errno.h>
16 #include <limits.h>
17
18 #include "splice.h"
19
20 static int do_splice(int infd, int outfd, unsigned int len, char *msg)
21 {
22         while (len) {
23                 int written = splice(infd, NULL, outfd, NULL, len, 0);
24
25                 if (written <= 0)
26                         return error(msg);
27
28                 len -= written;
29         }
30
31         return 0;
32 }
33
34 static int usage(char *name)
35 {
36         fprintf(stderr, "... | %s: hostname:port\n", name);
37         return 1;
38 }
39
40 int main(int argc, char *argv[])
41 {
42         struct sockaddr_in addr;
43         char *p, *hname;
44         struct stat sb;
45         int fd;
46
47         if (argc < 2)
48                 return usage(argv[0]);
49
50         if (fstat(STDIN_FILENO, &sb) < 0)
51                 return error("stat");
52         if (!S_ISFIFO(sb.st_mode)) {
53                 fprintf(stderr, "stdin must be a pipe\n");
54                 return usage(argv[0]);
55         }
56
57         hname = strdup(argv[1]);
58         p = strstr(hname, ":");
59         if (!p)
60                 return usage(argv[0]);
61
62         memset(&addr, 0, sizeof(addr));
63         addr.sin_family = AF_INET;
64         addr.sin_port = htons(atoi(p + 1));
65         *p = '\0';
66
67         if (inet_aton(hname, &addr.sin_addr) != 1) {
68                 struct hostent *hent = gethostbyname(hname);
69
70                 if (!hent)
71                         return error("gethostbyname");
72
73                 memcpy(&addr.sin_addr, hent->h_addr, 4);
74         }
75
76         fd = socket(AF_INET, SOCK_STREAM, 0);
77         if (fd < 0)
78                 return error("socket");
79
80         if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
81                 return error("connect");
82
83         do {
84                 int tee_len = tee(STDIN_FILENO, STDOUT_FILENO, INT_MAX, SPLICE_F_NONBLOCK);
85
86                 if (tee_len < 0) {
87                         if (errno == EAGAIN) {
88                                 usleep(1000);
89                                 continue;
90                         }
91                         return error("tee");
92                 } else if (!tee_len)
93                         break;
94
95                 /*
96                  * Send output to file, also consumes input pipe.
97                  */
98                 if (do_splice(STDIN_FILENO, fd, tee_len, "splice-net"))
99                         break;
100         } while (1);
101
102         close(fd);
103         return 0;
104 }