mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-01-01 05:16:05 +01:00
330 lines
7 KiB
C
330 lines
7 KiB
C
/*
|
|
* socket.c - Unix domain socket module
|
|
*
|
|
* This file is part of zsh, the Z shell.
|
|
*
|
|
* Copyright (c) 2002 Peter Stephenson
|
|
* All rights reserved.
|
|
*
|
|
* Permission is hereby granted, without written agreement and without
|
|
* license or royalty fees, to use, copy, modify, and distribute this
|
|
* software and to distribute modified versions of this software for any
|
|
* purpose, provided that the above copyright notice and the following
|
|
* two paragraphs appear in all copies of this software.
|
|
*
|
|
* In no event shall Peter Stephenson or the Zsh Development
|
|
* Group be liable to any party for direct, indirect, special, incidental,
|
|
* or consequential damages arising out of the use of this software and
|
|
* its documentation, even if Peter Stephenson, and the Zsh
|
|
* Development Group have been advised of the possibility of such damage.
|
|
*
|
|
* Peter Stephenson and the Zsh Development Group specifically
|
|
* disclaim any warranties, including, but not limited to, the implied
|
|
* warranties of merchantability and fitness for a particular purpose. The
|
|
* software provided hereunder is on an "as is" basis, and Peter Stephenson
|
|
* and the Zsh Development Group have no obligation to provide maintenance,
|
|
* support, updates, enhancements, or modifications.
|
|
*
|
|
*/
|
|
|
|
#include "socket.mdh"
|
|
#include "socket.pro"
|
|
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
|
|
/*
|
|
* We need to include the zsh headers later to avoid clashes with
|
|
* the definitions on some systems, however we need the configuration
|
|
* file to decide whether we can include netinet/in_systm.h, which
|
|
* doesn't exist on cygwin.
|
|
*/
|
|
|
|
/*
|
|
* We use poll() in preference to select because some subset of manuals says
|
|
* that's the thing to do, plus it's a bit less fiddly. I don't actually
|
|
* have access to a system with poll but not select, however, though
|
|
* both bits of the code have been tested on a machine with both.
|
|
*/
|
|
#ifdef HAVE_POLL_H
|
|
# include <poll.h>
|
|
#endif
|
|
#if defined(HAVE_POLL) && !defined(POLLIN) && !defined(POLLNORM)
|
|
# undef HAVE_POLL
|
|
#endif
|
|
|
|
static int
|
|
bin_zsocket(char *nam, char **args, Options ops, UNUSED(int func))
|
|
{
|
|
int err=1, verbose=0, test=0, targetfd=0;
|
|
ZSOCKLEN_T len;
|
|
struct sockaddr_un soun;
|
|
int sfd;
|
|
|
|
if (OPT_ISSET(ops,'v'))
|
|
verbose = 1;
|
|
|
|
if (OPT_ISSET(ops,'t'))
|
|
test = 1;
|
|
|
|
if (OPT_ISSET(ops,'d')) {
|
|
targetfd = atoi(OPT_ARG(ops,'d'));
|
|
if (!targetfd) {
|
|
zwarnnam(nam, "%s is an invalid argument to -d",
|
|
OPT_ARG(ops, 'd'));
|
|
return 1;
|
|
}
|
|
if (targetfd <= max_zsh_fd && fdtable[targetfd] != FDT_UNUSED) {
|
|
zwarnnam(nam, "file descriptor %d is in use by the shell",
|
|
targetfd);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (OPT_ISSET(ops,'l')) {
|
|
char *localfn;
|
|
|
|
if (!args[0]) {
|
|
zwarnnam(nam, "-l requires an argument");
|
|
return 1;
|
|
}
|
|
|
|
localfn = args[0];
|
|
|
|
sfd = socket(PF_UNIX, SOCK_STREAM, 0);
|
|
|
|
if (sfd == -1) {
|
|
zwarnnam(nam, "socket error: %e ", errno);
|
|
return 1;
|
|
}
|
|
|
|
soun.sun_family = AF_UNIX;
|
|
strncpy(soun.sun_path, localfn, sizeof(soun.sun_path)-1);
|
|
|
|
if (bind(sfd, (struct sockaddr *)&soun, sizeof(struct sockaddr_un)))
|
|
{
|
|
zwarnnam(nam, "could not bind to %s: %e", soun.sun_path, errno);
|
|
close(sfd);
|
|
return 1;
|
|
}
|
|
|
|
if (listen(sfd, 1))
|
|
{
|
|
zwarnnam(nam, "could not listen on socket: %e", errno);
|
|
close(sfd);
|
|
return 1;
|
|
}
|
|
|
|
addmodulefd(sfd, FDT_EXTERNAL);
|
|
|
|
if (targetfd) {
|
|
sfd = redup(sfd, targetfd);
|
|
}
|
|
else {
|
|
/* move the fd since no one will want to read from it */
|
|
sfd = movefd(sfd);
|
|
}
|
|
if (sfd == -1) {
|
|
zerrnam(nam, "cannot duplicate fd %d: %e", sfd, errno);
|
|
return 1;
|
|
}
|
|
|
|
/* allow to be closed explicitly */
|
|
fdtable[sfd] = FDT_EXTERNAL;
|
|
|
|
setiparam_no_convert("REPLY", (zlong)sfd);
|
|
|
|
if (verbose)
|
|
printf("%s listener is on fd %d\n", soun.sun_path, sfd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
else if (OPT_ISSET(ops,'a'))
|
|
{
|
|
int lfd, rfd;
|
|
|
|
if (!args[0]) {
|
|
zwarnnam(nam, "-a requires an argument");
|
|
return 1;
|
|
}
|
|
|
|
lfd = atoi(args[0]);
|
|
|
|
if (!lfd) {
|
|
zwarnnam(nam, "invalid numerical argument");
|
|
return 1;
|
|
}
|
|
|
|
if (test) {
|
|
#if defined(HAVE_POLL) || defined(HAVE_SELECT)
|
|
# ifdef HAVE_POLL
|
|
struct pollfd pfd;
|
|
int ret;
|
|
|
|
pfd.fd = lfd;
|
|
pfd.events = POLLIN;
|
|
if ((ret = poll(&pfd, 1, 0)) == 0) return 1;
|
|
else if (ret == -1)
|
|
{
|
|
zwarnnam(nam, "poll error: %e", errno);
|
|
return 1;
|
|
}
|
|
# else
|
|
fd_set rfds;
|
|
struct timeval tv;
|
|
int ret;
|
|
|
|
FD_ZERO(&rfds);
|
|
FD_SET(lfd, &rfds);
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 0;
|
|
|
|
if ((ret = select(lfd+1, &rfds, NULL, NULL, &tv)) == 0) return 1;
|
|
else if (ret == -1)
|
|
{
|
|
zwarnnam(nam, "select error: %e", errno);
|
|
return 1;
|
|
}
|
|
|
|
# endif
|
|
|
|
#else
|
|
zwarnnam(nam, "not currently supported");
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
len = sizeof(soun);
|
|
do {
|
|
rfd = accept(lfd, (struct sockaddr *)&soun, &len);
|
|
} while (rfd < 0 && errno == EINTR && !errflag);
|
|
|
|
if (rfd == -1) {
|
|
zwarnnam(nam, "could not accept connection: %e", errno);
|
|
return 1;
|
|
}
|
|
|
|
addmodulefd(rfd, FDT_EXTERNAL);
|
|
|
|
if (targetfd) {
|
|
sfd = redup(rfd, targetfd);
|
|
if (sfd < 0) {
|
|
zerrnam(nam, "could not duplicate socket fd to %d: %e", targetfd, errno);
|
|
zclose(rfd);
|
|
return 1;
|
|
}
|
|
fdtable[sfd] = FDT_EXTERNAL;
|
|
}
|
|
else {
|
|
sfd = rfd;
|
|
}
|
|
|
|
setiparam_no_convert("REPLY", (zlong)sfd);
|
|
|
|
if (verbose)
|
|
printf("new connection from %s is on fd %d\n", soun.sun_path, sfd);
|
|
}
|
|
else
|
|
{
|
|
if (!args[0]) {
|
|
zwarnnam(nam, "zsocket requires an argument");
|
|
return 1;
|
|
}
|
|
|
|
sfd = socket(PF_UNIX, SOCK_STREAM, 0);
|
|
|
|
if (sfd == -1) {
|
|
zwarnnam(nam, "socket creation failed: %e", errno);
|
|
return 1;
|
|
}
|
|
|
|
soun.sun_family = AF_UNIX;
|
|
strncpy(soun.sun_path, args[0], sizeof(soun.sun_path)-1);
|
|
|
|
if ((err = connect(sfd, (struct sockaddr *)&soun, sizeof(struct sockaddr_un)))) {
|
|
zwarnnam(nam, "connection failed: %e", errno);
|
|
close(sfd);
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
addmodulefd(sfd, FDT_EXTERNAL);
|
|
|
|
if (targetfd) {
|
|
if (redup(sfd, targetfd) < 0) {
|
|
zerrnam(nam, "could not duplicate socket fd to %d: %e", targetfd, errno);
|
|
zclose(sfd);
|
|
return 1;
|
|
}
|
|
sfd = targetfd;
|
|
fdtable[sfd] = FDT_EXTERNAL;
|
|
}
|
|
|
|
setiparam_no_convert("REPLY", (zlong)sfd);
|
|
|
|
if (verbose)
|
|
printf("%s is now on fd %d\n", soun.sun_path, sfd);
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin bintab[] = {
|
|
BUILTIN("zsocket", 0, bin_zsocket, 0, 3, 0, "ad:ltv", NULL),
|
|
};
|
|
|
|
static struct features module_features = {
|
|
bintab, sizeof(bintab)/sizeof(*bintab),
|
|
NULL, 0,
|
|
NULL, 0,
|
|
NULL, 0,
|
|
0
|
|
};
|
|
|
|
/* The load/unload routines required by the zsh library interface */
|
|
|
|
/**/
|
|
int
|
|
setup_(UNUSED(Module m))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**/
|
|
int
|
|
features_(Module m, char ***features)
|
|
{
|
|
*features = featuresarray(m, &module_features);
|
|
return 0;
|
|
}
|
|
|
|
/**/
|
|
int
|
|
enables_(Module m, int **enables)
|
|
{
|
|
return handlefeatures(m, &module_features, enables);
|
|
}
|
|
|
|
/**/
|
|
int
|
|
boot_(UNUSED(Module m))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**/
|
|
int
|
|
cleanup_(Module m)
|
|
{
|
|
return setfeatureenables(m, &module_features, NULL);
|
|
}
|
|
|
|
/**/
|
|
int
|
|
finish_(UNUSED(Module m))
|
|
{
|
|
return 0;
|
|
}
|