source: neercs/trunk/src/mini-socket.c @ 4783

Last change on this file since 4783 was 4783, checked in by Sam Hocevar, 9 years ago

mini: tweak stuff in the mini server / mini client test programs.

  • Property svn:keywords set to Id
File size: 7.3 KB
Line 
1/*
2 *  neercs        console-based window manager
3 *  Copyright (c) 2006-2011 Sam Hocevar <sam@hocevar.net>
4 *                2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org>
5 *                2008-2010 Pascal Terjan <pterjan@linuxfr.org>
6 *                All Rights Reserved
7 *
8 *  This program is free software. It comes without any warranty, to
9 *  the extent permitted by applicable law. You can redistribute it
10 *  and/or modify it under the terms of the Do What The Fuck You Want
11 *  To Public License, Version 2, as published by Sam Hocevar. See
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15#if defined HAVE_CONFIG_H
16#   include "config.h"
17#endif
18
19#include <stdio.h> /* perror() */
20#include <stdlib.h> /* malloc(), free() */
21#include <unistd.h> /* unlink() */
22#include <fcntl.h> /* fcntl() */
23#include <string.h> /* memcpy() */
24#include <sys/select.h> /* select() */
25#include <sys/types.h> /* bind(), connect() */
26#include <sys/socket.h> /* bind(), connect() */
27#include <sys/un.h> /* AF_UNIX */
28#include <errno.h> /* AF_UNIX */
29#include <time.h> /* time */
30
31#include "mini-neercs.h"
32#include "mini-socket.h"
33
34#define SIZEOF_SUN_PATH (sizeof(((struct sockaddr_un *)NULL)->sun_path))
35
36#define offsetof(s, f) ((int)(intptr_t)((s *)NULL)->f)
37
38struct nrx_socket
39{
40#if 1
41    /* Linux sockets */
42    int fd;
43    int server;
44    int connected;
45    char path[SIZEOF_SUN_PATH];
46#else
47#   error No socket implementation
48#endif
49};
50
51#define QLEN 10
52
53int
54serv_listen(const char *name)
55{
56    int                 fd, len, err, rval;
57    struct sockaddr_un  un;
58
59    /* create a UNIX domain stream socket */
60    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
61       return(-1);
62    unlink(name);   /* in case it already exists */
63
64    /* fill in socket address structure */
65    memset(&un, 0, sizeof(un));
66    un.sun_family = AF_UNIX;
67    strcpy(un.sun_path, name);
68    len = offsetof(struct sockaddr_un, sun_path) + strlen(name);
69
70    /* bind the name to the descriptor */
71    if (bind(fd, (struct sockaddr *)&un, len) < 0) {
72        rval = -2;
73        goto errout;
74    }
75    if (listen(fd, QLEN) < 0) { /* tell kernel we're a server */
76        rval = -3;
77        goto errout;
78    }
79    return(fd);
80
81errout:
82    err = errno;
83    close(fd);
84    errno = err;
85    return(rval);
86}
87
88#define STALE   30  /* client's name can't be older than this (sec) */
89
90/*
91 * Wait for a client connection to arrive, and accept it.
92 * We also obtain the client's user ID from the pathname
93 * that it must bind before calling us.
94 * Returns new fd if all OK, <0 on error
95 */
96int
97serv_accept(int listenfd, uid_t *uidptr)
98{
99    int                 clifd, len, err, rval;
100    time_t              staletime;
101    struct sockaddr_un  un;
102    struct stat         statbuf;
103
104    len = sizeof(un);
105    if ((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < 0)
106        return(-1);     /* often errno=EINTR, if signal caught */
107
108    /* obtain the client's uid from its calling address */
109    len -= offsetof(struct sockaddr_un, sun_path); /* len of pathname */
110    un.sun_path[len] = 0;           /* null terminate */
111
112    if (stat(un.sun_path, &statbuf) < 0) {
113        rval = -2;
114        goto errout;
115    }
116#ifdef S_ISSOCK     /* not defined for SVR4 */
117    if (S_ISSOCK(statbuf.st_mode) == 0) {
118        rval = -3;      /* not a socket */
119        goto errout;
120    }
121#endif
122    if ((statbuf.st_mode & (S_IRWXG | S_IRWXO)) ||
123        (statbuf.st_mode & S_IRWXU) != S_IRWXU) {
124          rval = -4;     /* is not rwx------ */
125          goto errout;
126    }
127
128    staletime = time(NULL) - STALE;
129    if (statbuf.st_atime < staletime ||
130        statbuf.st_ctime < staletime ||
131        statbuf.st_mtime < staletime) {
132          rval = -5;    /* i-node is too old */
133          goto errout;
134    }
135    if (uidptr != NULL)
136        *uidptr = statbuf.st_uid;   /* return uid of caller */
137    unlink(un.sun_path);        /* we're done with pathname now */
138    return(clifd);
139
140errout:
141    err = errno;
142    close(clifd);
143    errno = err;
144    return(rval);
145}
146
147#define CLI_PATH    "/var/tmp/"      /* +5 for pid = 14 chars */
148#define CLI_PERM    S_IRWXU          /* rwx for user only */
149
150/*
151 * Create a client endpoint and connect to a server.
152 * Returns fd if all OK, <0 on error.
153 */
154int
155cli_conn(const char *name)
156{
157    int                fd, len, err, rval;
158    struct sockaddr_un un;
159
160    /* create a UNIX domain stream socket */
161    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
162        return(-1);
163
164    /* fill socket address structure with our address */
165    memset(&un, 0, sizeof(un));
166    un.sun_family = AF_UNIX;
167    sprintf(un.sun_path, "%s%05d", CLI_PATH, getpid());
168    len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);
169
170    unlink(un.sun_path);        /* in case it already exists */
171    if (bind(fd, (struct sockaddr *)&un, len) < 0) {
172        rval = -2;
173        goto errout;
174    }
175    if (chmod(un.sun_path, CLI_PERM) < 0) {
176        rval = -3;
177        goto errout;
178    }
179    /* fill socket address structure with server's address */
180    memset(&un, 0, sizeof(un));
181    un.sun_family = AF_UNIX;
182    strcpy(un.sun_path, name);
183    len = offsetof(struct sockaddr_un, sun_path) + strlen(name);
184    if (connect(fd, (struct sockaddr *)&un, len) < 0) {
185        rval = -4;
186        goto errout;
187    }
188    return(fd);
189
190errout:
191    err = errno;
192    close(fd);
193    errno = err;
194    return(rval);
195}
196
197nrx_socket_t * socket_open(char const *path, int server)
198{
199    nrx_socket_t * sock;
200    struct sockaddr_un addr;
201    int ret, fd;
202
203#if 0
204    fd = socket(AF_UNIX, SOCK_STREAM, 0);
205    //fd = socket(AF_UNIX, SOCK_DGRAM, 0);
206    if (fd < 0)
207    {
208        perror("socket creation");
209        return NULL;
210    }
211
212    memset(&addr, 0, sizeof(struct sockaddr_un));
213    addr.sun_family = AF_UNIX;
214    strncpy(addr.sun_path, path, SIZEOF_SUN_PATH - 1);
215
216    if (server)
217    {
218        unlink(path);
219        ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
220    }
221    else
222    {
223        ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
224    }
225
226    if (ret < 0)
227    {
228        perror(server ? "socket binding" : "socket connection");
229        close(fd);
230        return NULL;
231    }
232
233    fcntl(fd, F_SETFL, O_NONBLOCK);
234#endif
235if (server)
236    fd = serv_listen(path);
237else
238    fd = cli_conn(path);
239if (fd < 0) return NULL;
240
241    sock = malloc(sizeof(*sock));
242    sock->fd = fd;
243    sock->server = server;
244    sock->connected = 0;
245    strncpy(sock->path, path, SIZEOF_SUN_PATH - 1);
246
247    return sock;
248}
249
250int socket_select(nrx_socket_t *sock, int usecs)
251{
252    fd_set rfds;
253    struct timeval tv;
254    int ret;
255
256    FD_ZERO(&rfds);
257    FD_SET(sock->fd, &rfds);
258
259    tv.tv_sec = usecs / 1000000;
260    tv.tv_usec = usecs % 1000000;
261
262    ret = select(sock->fd + 1, &rfds, NULL, NULL, &tv);
263    if (ret < 0)
264        return -1;
265
266    if (FD_ISSET(sock->fd, &rfds))
267        return 1;
268
269    return 0;
270}
271
272int socket_puts(nrx_socket_t *sock, char const *str)
273{
274    int ret;
275fprintf(stderr, "pid %i sending %i bytes on %s: %s\n", getpid(), (int)strlen(str), sock->path, str);
276    ret = write(sock->fd, str, strlen(str));
277    return ret;
278}
279
280ssize_t socket_read(nrx_socket_t *sock, void *buf, size_t count)
281{
282    int ret;
283    ret = read(sock->fd, buf, count);
284if (ret >= 0) ((char *)buf)[ret] = 0;
285if (ret >= 0) fprintf(stderr, "pid %i recving %i bytes on %s: %s\n", getpid(), ret, sock->path, (char *)buf);
286    return ret;
287}
288
289void socket_close(nrx_socket_t *sock)
290{
291    close(sock->fd);
292    if (sock->server)
293        unlink(sock->path);
294    free(sock);
295}
296
Note: See TracBrowser for help on using the repository browser.