1/*
2 * socket.c - Unix domain socket module
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 2002 Peter Stephenson
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Peter Stephenson or the Zsh Development
16 * Group be liable to any party for direct, indirect, special, incidental,
17 * or consequential damages arising out of the use of this software and
18 * its documentation, even if Peter Stephenson, and the Zsh
19 * Development Group have been advised of the possibility of such damage.
20 *
21 * Peter Stephenson and the Zsh Development Group specifically
22 * disclaim any warranties, including, but not limited to, the implied
23 * warranties of merchantability and fitness for a particular purpose.  The
24 * software provided hereunder is on an "as is" basis, and Peter Stephenson
25 * and the Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
27 *
28 */
29
30#include "socket.mdh"
31#include "socket.pro"
32
33#include <sys/socket.h>
34#include <sys/un.h>
35
36/*
37 * We need to include the zsh headers later to avoid clashes with
38 * the definitions on some systems, however we need the configuration
39 * file to decide whether we can include netinet/in_systm.h, which
40 * doesn't exist on cygwin.
41 */
42
43/*
44 * We use poll() in preference to select because some subset of manuals says
45 * that's the thing to do, plus it's a bit less fiddly.  I don't actually
46 * have access to a system with poll but not select, however, though
47 * both bits of the code have been tested on a machine with both.
48 */
49#ifdef HAVE_POLL_H
50# include <poll.h>
51#endif
52#if defined(HAVE_POLL) && !defined(POLLIN) && !defined(POLLNORM)
53# undef HAVE_POLL
54#endif
55
56static int
57bin_zsocket(char *nam, char **args, Options ops, UNUSED(int func))
58{
59    int err=1, verbose=0, test=0, targetfd=0;
60    ZSOCKLEN_T len;
61    struct sockaddr_un soun;
62    int sfd;
63
64    if (OPT_ISSET(ops,'v'))
65	verbose = 1;
66
67    if (OPT_ISSET(ops,'t'))
68	test = 1;
69
70    if (OPT_ISSET(ops,'d')) {
71	targetfd = atoi(OPT_ARG(ops,'d'));
72	if (!targetfd) {
73	    zwarnnam(nam, "%s is an invalid argument to -d",
74		     OPT_ARG(ops, 'd'));
75	    return 1;
76	}
77	if (targetfd <= max_zsh_fd && fdtable[targetfd] != FDT_UNUSED) {
78	    zwarnnam(nam, "file descriptor %d is in use by the shell",
79		     targetfd);
80	    return 1;
81	}
82    }
83
84    if (OPT_ISSET(ops,'l')) {
85	char *localfn;
86
87	if (!args[0]) {
88	    zwarnnam(nam, "-l requires an argument");
89	    return 1;
90	}
91
92	localfn = args[0];
93
94	sfd = socket(PF_UNIX, SOCK_STREAM, 0);
95
96	if (sfd == -1) {
97	    zwarnnam(nam, "socket error: %e ", errno);
98	    return 1;
99	}
100
101	soun.sun_family = AF_UNIX;
102	strncpy(soun.sun_path, localfn, sizeof(soun.sun_path)-1);
103
104	if (bind(sfd, (struct sockaddr *)&soun, sizeof(struct sockaddr_un)))
105	{
106	    zwarnnam(nam, "could not bind to %s: %e", soun.sun_path, errno);
107	    close(sfd);
108	    return 1;
109	}
110
111	if (listen(sfd, 1))
112	{
113	    zwarnnam(nam, "could not listen on socket: %e", errno);
114	    close(sfd);
115	    return 1;
116	}
117
118	if (targetfd) {
119	    sfd = redup(sfd, targetfd);
120	}
121	else {
122	    /* move the fd since no one will want to read from it */
123	    sfd = movefd(sfd);
124	}
125	if (sfd == -1) {
126	    zerrnam(nam, "cannot duplicate fd %d: %e", sfd, errno);
127	    return 1;
128	}
129
130	setiparam("REPLY", sfd);
131
132	if (verbose)
133	    printf("%s listener is on fd %d\n", soun.sun_path, sfd);
134
135	return 0;
136
137    }
138    else if (OPT_ISSET(ops,'a'))
139    {
140	int lfd, rfd;
141
142	if (!args[0]) {
143	    zwarnnam(nam, "-a requires an argument");
144	    return 1;
145	}
146
147	lfd = atoi(args[0]);
148
149	if (!lfd) {
150	    zwarnnam(nam, "invalid numerical argument");
151	    return 1;
152	}
153
154	if (test) {
155#if defined(HAVE_POLL) || defined(HAVE_SELECT)
156# ifdef HAVE_POLL
157	    struct pollfd pfd;
158	    int ret;
159
160	    pfd.fd = lfd;
161	    pfd.events = POLLIN;
162	    if ((ret = poll(&pfd, 1, 0)) == 0) return 1;
163	    else if (ret == -1)
164	    {
165		zwarnnam(nam, "poll error: %e", errno);
166		return 1;
167	    }
168# else
169	    fd_set rfds;
170	    struct timeval tv;
171	    int ret;
172
173	    FD_ZERO(&rfds);
174	    FD_SET(lfd, &rfds);
175	    tv.tv_sec = 0;
176	    tv.tv_usec = 0;
177
178	    if ((ret = select(lfd+1, &rfds, NULL, NULL, &tv))) return 1;
179	    else if (ret == -1)
180	    {
181		zwarnnam(nam, "select error: %e", errno);
182		return 1;
183	    }
184
185# endif
186
187#else
188	    zwarnnam(nam, "not currently supported");
189	    return 1;
190#endif
191	}
192
193	len = sizeof(soun);
194	if ((rfd = accept(lfd, (struct sockaddr *)&soun, &len)) == -1)
195	{
196	    zwarnnam(nam, "could not accept connection: %e", errno);
197	    return 1;
198	}
199
200	if (targetfd) {
201	    sfd = redup(rfd, targetfd);
202	    if (sfd < 0) {
203		zerrnam(nam, "could not duplicate socket fd to %d: %e", targetfd, errno);
204		return 1;
205	    }
206	}
207	else {
208	    sfd = rfd;
209	}
210
211	setiparam("REPLY", sfd);
212
213	if (verbose)
214	    printf("new connection from %s is on fd %d\n", soun.sun_path, sfd);
215    }
216    else
217    {
218	if (!args[0]) {
219	    zwarnnam(nam, "zsocket requires an argument");
220	    return 1;
221	}
222
223	sfd = socket(PF_UNIX, SOCK_STREAM, 0);
224
225	if (sfd == -1) {
226	    zwarnnam(nam, "socket creation failed: %e", errno);
227	    return 1;
228	}
229
230	soun.sun_family = AF_UNIX;
231	strncpy(soun.sun_path, args[0], sizeof(soun.sun_path)-1);
232
233	if ((err = connect(sfd, (struct sockaddr *)&soun, sizeof(struct sockaddr_un)))) {
234	    zwarnnam(nam, "connection failed: %e", errno);
235	    close(sfd);
236	    return 1;
237	}
238	else
239	{
240	    if (targetfd) {
241		sfd = redup(sfd, targetfd);
242		if (sfd < 0) {
243		    zerrnam(nam, "could not duplicate socket fd to %d: %e", targetfd, errno);
244		    return 1;
245		}
246	    }
247
248	    setiparam("REPLY", sfd);
249
250	    if (verbose)
251		printf("%s is now on fd %d\n", soun.sun_path, sfd);
252	}
253
254    }
255
256    return 0;
257}
258
259static struct builtin bintab[] = {
260    BUILTIN("zsocket", 0, bin_zsocket, 0, 3, 0, "ad:ltv", NULL),
261};
262
263static struct features module_features = {
264    bintab, sizeof(bintab)/sizeof(*bintab),
265    NULL, 0,
266    NULL, 0,
267    NULL, 0,
268    0
269};
270
271/* The load/unload routines required by the zsh library interface */
272
273/**/
274int
275setup_(UNUSED(Module m))
276{
277    return 0;
278}
279
280/**/
281int
282features_(Module m, char ***features)
283{
284    *features = featuresarray(m, &module_features);
285    return 0;
286}
287
288/**/
289int
290enables_(Module m, int **enables)
291{
292    return handlefeatures(m, &module_features, enables);
293}
294
295/**/
296int
297boot_(Module m)
298{
299    return 0;
300}
301
302/**/
303int
304cleanup_(Module m)
305{
306    return setfeatureenables(m, &module_features, NULL);
307}
308
309/**/
310int
311finish_(UNUSED(Module m))
312{
313    return 0;
314}
315