source: trunk/base/src/pextlib1.0/tracelib.c @ 108069

Last change on this file since 108069 was 108069, checked in by jmr@…, 7 years ago

check for strlcat and provide an implementation when not present in the OS

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 30.2 KB
Line 
1/* # -*- coding: utf-8; mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=c:et:sw=4:ts=4:sts=4
2 */
3/*
4 * tracelib.c
5 * $Id: tracelib.c 108069 2013-07-12 05:28:37Z jmr@macports.org $
6 *
7 * Copyright (c) 2007-2008 Eugene Pimenov (GSoC)
8 * Copyright (c) 2008-2010, 2012-2013 The MacPorts Project
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the MacPorts Team nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36#if HAVE_CONFIG_H
37#include <config.h>
38#endif
39
40#include <errno.h>
41#include <fcntl.h>
42#include <limits.h>
43#include <pthread.h>
44#include <stdarg.h>
45#include <stdint.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <sys/event.h>
50#include <sys/resource.h>
51#include <sys/socket.h>
52#include <sys/time.h>
53#include <sys/types.h>
54#include <sys/un.h>
55#include <unistd.h>
56
57#include <cregistry/entry.h>
58#include <registry2.0/registry.h>
59
60#include "tracelib.h"
61
62#include "strlcat.h"
63
64#ifndef HAVE_STRLCPY
65/* Define strlcpy if it's not available. */
66size_t strlcpy(char *dst, const char *src, size_t size);
67size_t strlcpy(char *dst, const char *src, size_t size) {
68    size_t result = strlen(src);
69    if (size > 0) {
70        size_t copylen = size - 1;
71        if (copylen > result) {
72            copylen = result;
73        }
74        memcpy(dst, src, copylen);
75        dst[copylen] = 0;
76    }
77    return result;
78}
79#endif
80
81static char *name;
82static char *sandbox;
83static char *filemap, *filemap_end;
84static char *depends;
85static int sock = -1;
86static int kq = -1;
87#ifndef EVFILT_USER
88/* if EVFILT_USER isn't available (< 10.6), use the self-pipe trick to return
89 * from the blocking kqueue(2) call by writing a byte to the pipe */
90static int selfpipe[2];
91#endif
92static int enable_fence = 0;
93static Tcl_Interp *interp;
94static pthread_mutex_t sock_mutex = PTHREAD_MUTEX_INITIALIZER;
95static int cleanuping = 0;
96static char *sdk = NULL;
97
98static void send_file_map(int sock);
99static void dep_check(int sock, char *path);
100static void sandbox_violation(int sock, const char *path);
101static void ui_warn(const char *format, ...);
102#if 0
103static void ui_info(const char *format, ...);
104#endif
105static void ui_error(const char *format, ...);
106
107#define MAX_SOCKETS (1024)
108#define BUFSIZE     (1024)
109
110/**
111 * send a buffer \c buf with the given length \c size to the socket \c sock, by
112 * using the communication protocol between darwintrace and tracelib (i.e., by
113 * prefixing the code with a uint32_t containing the length of the message)
114 *
115 * \param[in] sock the socket to send to
116 * \param[in] buf the buffer to send, should contain at least \c size bytes
117 * \param[in] size the number of bytes in \c buf
118 */
119static void answer_s(int sock, const char *buf, uint32_t size) {
120    send(sock, &size, sizeof(size), 0);
121    send(sock, buf, size, 0);
122}
123
124/**
125 * send a '\0'-terminated string given in \c buf to the socket \c by using the
126 * communication protocol between darwintrace and tracelib. See \c answer_s for
127 * details.
128 *
129 * \param[in] sock the socket to send to
130 * \param[in] buf the string to send; must be \0-terminated
131 */
132static void answer(int sock, const char *buf) {
133    answer_s(sock, buf, (uint32_t) strlen(buf));
134}
135
136/**
137 * Sets the path of the tracelib unix socket where darwintrace should attempt
138 * to connect to. This path should be specific to the port being installed.
139 * Different sockets should be used for different ports (and maybe even
140 * phases).
141 *
142 * \param[inout] interp the Tcl interpreter
143 * \param[in] objc the number of parameters
144 * \param[in] the parameters
145 * \return a Tcl return code
146 */
147static int TracelibSetNameCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
148    if (objc != 3) {
149        Tcl_WrongNumArgs(interp, 2, objv, "number of arguments should be exactly 3");
150        return TCL_ERROR;
151    }
152
153    name = strdup(Tcl_GetString(objv[2]));
154    if (!name) {
155        Tcl_SetResult(interp, "memory allocation failed", TCL_STATIC);
156        return TCL_ERROR;
157    }
158
159    return TCL_OK;
160}
161
162/**
163 * Save sandbox boundaries to memory and format them for darwintrace. This
164 * means changing : to \0 (with \ being an escape char).
165 *
166 * Input:
167 *  /dev/null:/dev/tty:/tmp\:
168 * In variable;
169 *  /dev/null\0/dev/tty\0/tmp:\0\0
170 *
171 * \param[inout] interp the Tcl interpreter
172 * \param[in] objc the number of parameters
173 * \param[in] the parameters
174 * \return a Tcl return code
175 */
176static int TracelibSetSandboxCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
177    int len;
178    char *src, *dst;
179    enum { NORMAL, ESCAPE } state = NORMAL;
180
181    if (objc != 3) {
182        Tcl_WrongNumArgs(interp, 2, objv, "number of arguments should be exactly 3");
183        return TCL_ERROR;
184    }
185
186    src = Tcl_GetString(objv[2]);
187    len = strlen(src) + 2;
188    sandbox = malloc(len);
189    if (!sandbox) {
190        Tcl_SetResult(interp, "memory allocation failed", TCL_STATIC);
191        return TCL_ERROR;
192    }
193    for (dst = sandbox; *src != '\0'; src++) {
194        switch (*src) {
195            case '\\':
196                if (state == ESCAPE) {
197                    /* double backslash, turn into single backslash (note
198                     * C strings use \ as escape char, too! */
199                    *dst++ = '\\';
200                    state = NORMAL;
201                } else {
202                    /* hit a backslash, assume this is an escape sequence */
203                    state = ESCAPE;
204                }
205                break;
206            case ':':
207                if (state == ESCAPE) {
208                    /* : was escaped, keep literally */
209                    *dst++ = ':';
210                    state = NORMAL;
211                } else {
212                    /* : -> \0, unless it has been escaped */
213                    *dst++ = '\0';
214                }
215                break;
216            default:
217                if (state == ESCAPE) {
218                    /* unknown escape sequence, free buffer and raise an error */
219                    free(sandbox);
220                    Tcl_SetResult(interp, "unknown escape sequence", TCL_STATIC);
221                    return TCL_ERROR;
222                }
223                /* otherwise: copy the char */
224                *dst++ = *src;
225                break;
226        }
227    }
228    /* add two \0 to mark the end */
229    *dst++ = '\0';
230    *dst = '\0';
231
232    return TCL_OK;
233}
234
235/**
236 * Receive line from socket, parse it and send an answer, if necessary. The
237 * caller should ensure that data is available for reading from the given
238 * socket. This method will block until a complete message has been read.
239 *
240 * \param[in] sock the socket to communicate with
241 * \return 1, if the communication was successful, 0 in case of errors and/or
242 *         when the socket should be closed
243 */
244static int process_line(int sock) {
245    char *f;
246    char buf[BUFSIZE];
247    uint32_t len;
248    ssize_t ret;
249
250    if ((ret = recv(sock, &len, sizeof(len), MSG_WAITALL)) != sizeof(len)) {
251        if (ret < 0) {
252            perror("tracelib: recv");
253        } else if (ret == 0) {
254            /* this usually means the socket was closed by the remote side */
255        } else {
256            fprintf(stderr, "tracelib: partial data received: expected %ld, but got %ld on socket %d\n", (unsigned long) sizeof(len), (unsigned long) ret, sock);
257        }
258        return 0;
259    }
260
261    if (len > BUFSIZE - 1) {
262        fprintf(stderr, "tracelib: transfer too large: %ld bytes sent, but buffer holds %d on socket %d\n", (unsigned long) len, (int) (BUFSIZE - 1), sock);
263        return 0;
264    }
265
266    if ((ret = recv(sock, buf, len, MSG_WAITALL)) != (ssize_t) len) {
267        if (ret < 0) {
268            perror("tracelib: recv");
269        } else {
270            fprintf(stderr, "tracelib: partial data received: expected %ld, but got %ld on socket %d\n", (unsigned long) len, (unsigned long) ret, sock);
271        }
272        return 0;
273    }
274    buf[len] = '\0';
275
276    f = strchr(buf, '\t');
277    if (!f) {
278        fprintf(stderr, "tracelib: malformed command '%s' from socket %d\n", buf, sock);
279        return 0;
280    }
281
282    /* Replace \t with \0 */
283    *f = '\0';
284    /* Advance pointer to arguments */
285    f++;
286
287    if (strcmp(buf, "filemap") == 0) {
288        send_file_map(sock);
289    } else if (strcmp(buf, "sandbox_violation") == 0) {
290        sandbox_violation(sock, f);
291    } else if (strcmp(buf, "dep_check") == 0) {
292        dep_check(sock, f);
293    } else {
294        fprintf(stderr, "tracelib: unexpected command %s (%s)\n", buf, f);
295        return 0;
296    }
297
298    return 1;
299}
300
301/**
302 * Construct an in-memory representation of the sandbox file map and send it to
303 * the socket indicated by \c sock.
304 *
305 * \param[in] sock the socket to send the sandbox bounds to
306 */
307static void send_file_map(int sock) {
308    if (!filemap) {
309        char *t, * _;
310
311        size_t remaining = 1024;
312        filemap = (char *)malloc(remaining);
313        if (!filemap) {
314            ui_warn("send_file_map: memory allocation failed");
315            return;
316        }
317        t = filemap;
318
319#       define append_allow(path, resolution) do { strlcpy(t, path, remaining); \
320            if (remaining < (strlen(t)+3)) \
321                remaining=0; \
322            else \
323                remaining-=strlen(t)+3; \
324            t+=strlen(t)+1; \
325            *t++=resolution; \
326            *t++=0; \
327        } while(0);
328
329        if (enable_fence) {
330            for (_ = sandbox; *_; _ += strlen(_) + 1) {
331                append_allow(_, 0);
332            }
333
334            append_allow("/bin", 0);
335            append_allow("/sbin", 0);
336            append_allow("/dev", 0);
337            append_allow(Tcl_GetVar(interp, "prefix", TCL_GLOBAL_ONLY), 2);
338            /* If there is no SDK we will allow everything in /usr /System/Library etc, else add binaries to allow, and redirect root to SDK. */
339            if (sdk && *sdk) {
340                char buf[260];
341                buf[0] = '\0';
342                strlcat(buf, Tcl_GetVar(interp, "developer_dir", TCL_GLOBAL_ONLY), 260);
343                strlcat(buf, "/SDKs/", 260);
344                strlcat(buf, sdk, 260);
345
346                append_allow("/usr/bin", 0);
347                append_allow("/usr/sbin", 0);
348                append_allow("/usr/libexec/gcc", 0);
349                append_allow("/System/Library/Perl", 0);
350                append_allow("/", 1);
351                strlcpy(t - 1, buf, remaining);
352                t += strlen(t) + 1;
353            } else {
354                append_allow("/usr", 0);
355                append_allow("/System/Library", 0);
356                append_allow("/Library", 0);
357                append_allow(Tcl_GetVar(interp, "developer_dir", TCL_GLOBAL_ONLY), 0);
358            }
359        } else {
360            append_allow("/", 0);
361        }
362        append_allow("", 0);
363        filemap_end = t;
364#       undef append_allow
365    }
366
367    answer_s(sock, filemap, filemap_end - filemap);
368}
369
370/**
371 * Process a sandbox violation reported by darwintrace. Calls back up to Tcl to
372 * run a callback with the reported violation path.
373 *
374 * \param[in] sock socket reporting the violation; unused.
375 * \param[in] path the offending path to be passed to the callback
376 */
377static void sandbox_violation(int sock UNUSED, const char *path) {
378    Tcl_SetVar(interp, "path", path, 0);
379    Tcl_Eval(interp, "slave_add_sandbox_violation $path");
380    Tcl_UnsetVar(interp, "path", 0);
381}
382
383/**
384 * Check whether a path is in the transitive hull of dependencies of the port
385 * currently being installed and send the result of the query back to the
386 * socket.
387 *
388 * Sends one of the following characters as return code to the socket:
389 *  - #: in case of errors. Not handled by the darwintrace code, which will
390 *       lead to an error and the termination of the processing that sent the
391 *       request causing this error.
392 *  - ?: if the file isn't known to MacPorts (i.e., not registered to any port)
393 *  - +: if the file was installed by a dependency and access should be granted
394 *  - !: if the file was installed by a MacPorts port which is not in the
395 *       transitive hull of dependencies and access should be denied.
396 *
397 * \param[in] sock the socket to answer to
398 * \param[in] path the path to return the dependency information for
399 */
400static void dep_check(int sock, char *path) {
401    char *port = 0;
402    char *t;
403    reg_registry *reg;
404    reg_entry entry;
405    reg_error error;
406
407    if (NULL == (reg = registry_for(interp, reg_attached))) {
408        ui_error(Tcl_GetStringResult(interp));
409        /* send unexpected output to make the build fail */
410        answer(sock, "#");
411    }
412
413    /* find the port id */
414    entry.reg = reg;
415    entry.proc = NULL;
416    entry.id = reg_entry_owner_id(reg, path);
417    if (entry.id == 0) {
418        /* file isn't known to MacPorts */
419        answer(sock, "?");
420        return;
421    }
422
423    /* find the port's name to compare with out list */
424    if (!reg_entry_propget(&entry, "name", &port, &error)) {
425        /* send unexpected output to make the build fail */
426        ui_error(error.description);
427        answer(sock, "#");
428    }
429
430    /* check our list of dependencies */
431    for (t = depends; *t; t += strlen(t) + 1) {
432        if (strcmp(t, port) == 0) {
433            free(port);
434            answer(sock, "+");
435            return;
436        }
437    }
438
439    free(port);
440    answer(sock, "!");
441}
442
443static void ui_msg(const char *severity, const char *format, va_list va) {
444    char buf[1024], tclcmd[32];
445
446    vsnprintf(buf, sizeof(buf), format, va);
447
448    snprintf(tclcmd, sizeof(tclcmd), "ui_%s $warn", severity);
449
450    Tcl_SetVar(interp, "warn", buf, 0);
451    if (TCL_OK != Tcl_Eval(interp, tclcmd)) {
452        fprintf(stderr, "Error evaluating tcl statement `%s': %s\n", tclcmd, Tcl_GetStringResult(interp));
453    }
454    Tcl_UnsetVar(interp, "warn", 0);
455
456}
457
458static void ui_warn(const char *format, ...) {
459    va_list va;
460
461    va_start(va, format);
462    ui_msg("warn", format, va);
463    va_end(va);
464}
465
466#if 0
467static void ui_info(const char *format, ...) {
468    va_list va;
469
470    va_start(va, format);
471    ui_msg("info", format, va);
472    va_end(va);
473}
474#endif
475
476static void ui_error(const char *format, ...) {
477    va_list va;
478    va_start(va, format);
479    ui_msg("error", format, va);
480    va_end(va);
481}
482
483static int TracelibOpenSocketCmd(Tcl_Interp *in) {
484    struct sockaddr_un sun;
485    struct rlimit rl;
486
487    cleanuping = 0;
488
489    pthread_mutex_lock(&sock_mutex);
490    if (-1 == (sock = socket(PF_LOCAL, SOCK_STREAM, 0))) {
491        Tcl_SetErrno(errno);
492        Tcl_ResetResult(interp);
493        Tcl_AppendResult(interp, "socket: ", (char *) Tcl_PosixError(interp), NULL);
494        pthread_mutex_unlock(&sock_mutex);
495        return TCL_ERROR;
496    }
497    pthread_mutex_unlock(&sock_mutex);
498
499    interp = in;
500
501    /* raise the limit of open files to the maximum from the default soft limit
502     * of 256 */
503    if (getrlimit(RLIMIT_NOFILE, &rl) == -1) {
504        ui_warn("getrlimit failed (%d), skipping setrlimit", errno);
505    } else {
506#if defined(__APPLE__) && defined(OPEN_MAX)
507        if (rl.rlim_max > OPEN_MAX) {
508            rl.rlim_max = OPEN_MAX;
509        }
510#endif
511        rl.rlim_cur = rl.rlim_max;
512        if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
513            ui_warn("setrlimit failed (%d)", errno);
514        }
515    }
516
517    sun.sun_family = AF_UNIX;
518    strlcpy(sun.sun_path, name, sizeof(sun.sun_path));
519
520    if (-1 == (bind(sock, (struct sockaddr *) &sun, sizeof(sun)))) {
521        Tcl_SetErrno(errno);
522        Tcl_ResetResult(interp);
523        Tcl_AppendResult(interp, "bind: ", (char *) Tcl_PosixError(interp), NULL);
524        close(sock);
525        sock = -1;
526        return TCL_ERROR;
527    }
528
529    if (-1 == listen(sock, 32)) {
530        Tcl_SetErrno(errno);
531        Tcl_ResetResult(interp);
532        Tcl_AppendResult(interp, "listen: ", (char *) Tcl_PosixError(interp), NULL);
533        close(sock);
534        sock = -1;
535        return TCL_ERROR;
536    }
537
538    return TCL_OK;
539}
540
541/* create this on heap rather than stack, due to its rather large size */
542static struct kevent res_kevents[MAX_SOCKETS];
543static int TracelibRunCmd(Tcl_Interp *in) {
544    struct kevent kev;
545    int flags;
546    int oldsock;
547    int opensockcount = 0;
548
549    if (-1 == (kq = kqueue())) {
550        Tcl_SetErrno(errno);
551        Tcl_ResetResult(in);
552        Tcl_AppendResult(in, "kqueue: ", (char *) Tcl_PosixError(in), NULL);
553        return TCL_ERROR;
554    }
555
556    pthread_mutex_lock(&sock_mutex);
557    if (sock != -1) {
558        oldsock = sock;
559
560        /* mark listen socket non-blocking in order to prevent a race condition
561         * that would occur between kevent(2) and accept(2), if a incoming
562         * connection is aborted before it is accepted. Using a non-blocking
563         * accept(2) prevents the problem.*/
564        flags = fcntl(oldsock, F_GETFL, 0);
565        if (-1 == fcntl(oldsock, F_SETFL, flags | O_NONBLOCK)) {
566            Tcl_SetErrno(errno);
567            Tcl_ResetResult(in);
568            Tcl_AppendResult(in, "fcntl(F_SETFL, += O_NONBLOCK): ", (char *) Tcl_PosixError(in), NULL);
569            return TCL_ERROR;
570        }
571
572        /* register the listen socket in the kqueue */
573        EV_SET(&kev, oldsock, EVFILT_READ, EV_ADD | EV_RECEIPT, 0, 0, NULL);
574        if (1 != kevent(kq, &kev, 1, &kev, 1, NULL)) {
575            Tcl_SetErrno(errno);
576            Tcl_ResetResult(in);
577            Tcl_AppendResult(in, "kevent: ", (char *) Tcl_PosixError(in), NULL);
578            close(kq);
579            return TCL_ERROR;
580        }
581        /* kevent(2) on EV_RECEIPT: When passed as input, it forces EV_ERROR to
582         * always be returned. When a filter is successfully added, the data field
583         * will be zero. */
584        if ((kev.flags & EV_ERROR) == 0 || ((kev.flags & EV_ERROR) > 0 && kev.data != 0)) {
585            Tcl_SetErrno(kev.data);
586            Tcl_ResetResult(in);
587            Tcl_AppendResult(in, "kevent: ", (char *) Tcl_PosixError(in), NULL);
588            close(kq);
589            return TCL_ERROR;
590        }
591
592
593#       ifndef EVFILT_USER
594        /* on systems that don't have EVFILT_USER, use the self-pipe trick to
595         * trigger returning from kevent(2) when tracelib closesocket is
596         * called. */
597        if (-1 == pipe(selfpipe)) {
598            Tcl_SetErrno(errno);
599            Tcl_ResetResult(in);
600            Tcl_AppendResult(in, "pipe: ", (char *) Tcl_PosixError(in), NULL);
601            return TCL_ERROR;
602        }
603
604        /* mark the write side of the pipe non-blocking */
605        flags = fcntl(selfpipe[1], F_GETFL, 0);
606        if (-1 == fcntl(selfpipe[1], F_SETFL, flags | O_NONBLOCK)) {
607            Tcl_SetErrno(errno);
608            Tcl_ResetResult(in);
609            Tcl_AppendResult(in, "fcntl(F_SETFL, += O_NONBLOCK): ", (char *) Tcl_PosixError(in), NULL);
610            return TCL_ERROR;
611        }
612
613        /* wait for the user event on the listen socket, as sent by CloseCmd as
614         * deathpill */
615        EV_SET(&kev, selfpipe[0], EVFILT_READ, EV_ADD | EV_RECEIPT, 0, 0, NULL);
616        if (1 != kevent(kq, &kev, 1, &kev, 1, NULL)) {
617            Tcl_SetErrno(errno);
618            Tcl_ResetResult(in);
619            Tcl_AppendResult(in, "kevent: ", (char *) Tcl_PosixError(in), NULL);
620            close(kq);
621            return TCL_ERROR;
622        }
623        /* kevent(2) on EV_RECEIPT: When passed as input, it forces EV_ERROR to
624         * always be returned. When a filter is successfully added, the data field
625         * will be zero. */
626        if ((kev.flags & EV_ERROR) == 0 || ((kev.flags & EV_ERROR) > 0 && kev.data != 0)) {
627            Tcl_SetErrno(kev.data);
628            Tcl_ResetResult(in);
629            Tcl_AppendResult(in, "kevent: ", (char *) Tcl_PosixError(in), NULL);
630            close(kq);
631            return TCL_ERROR;
632        }
633#       else /* ifndef EVFILT_USER */
634        /* wait for the user event on the listen socket, as sent by CloseCmd as
635         * deathpill */
636        EV_SET(&kev, oldsock, EVFILT_USER, EV_ADD | EV_RECEIPT, 0, 0, NULL);
637        if (1 != kevent(kq, &kev, 1, &kev, 1, NULL)) {
638            Tcl_SetErrno(errno);
639            Tcl_ResetResult(in);
640            Tcl_AppendResult(in, "kevent: ", (char *) Tcl_PosixError(in), NULL);
641            close(kq);
642            return TCL_ERROR;
643        }
644        /* kevent(2) on EV_RECEIPT: When passed as input, it forces EV_ERROR to
645         * always be returned. When a filter is successfully added, the data field
646         * will be zero. */
647        if ((kev.flags & EV_ERROR) == 0 || ((kev.flags & EV_ERROR) > 0 && kev.data != 0)) {
648            Tcl_SetErrno(kev.data);
649            Tcl_ResetResult(in);
650            Tcl_AppendResult(in, "kevent: ", (char *) Tcl_PosixError(in), NULL);
651            close(kq);
652            return TCL_ERROR;
653        }
654#       endif /* ifndef EVFILT_USER */
655    }
656    pthread_mutex_unlock(&sock_mutex);
657
658    while (sock != -1 && !cleanuping) {
659        int keventstatus;
660        int i;
661
662        /* run kevent(2) until new activity is available */
663        do {
664            if (-1 == (keventstatus = kevent(kq, NULL, 0, res_kevents, MAX_SOCKETS, NULL))) {
665                Tcl_SetErrno(errno);
666                Tcl_ResetResult(in);
667                Tcl_AppendResult(in, "kevent: ", (char *) Tcl_PosixError(in), NULL);
668                close(kq);
669                return TCL_ERROR;
670            }
671        } while (keventstatus == 0);
672
673        for (i = 0; i < keventstatus; ++i) {
674#           ifndef EVFILT_USER
675            /* handle traffic on the selfpipe */
676            if ((int) res_kevents[i].ident == selfpipe[0]) {
677                pthread_mutex_lock(&sock_mutex);
678                close(selfpipe[0]);
679                close(selfpipe[1]);
680                selfpipe[0] = -1;
681                selfpipe[1] = -1;
682                pthread_mutex_unlock(&sock_mutex);
683                break;
684            }
685#           endif
686            /* the control socket has activity – we might have a new
687             * connection. We use a copy of sock here, because sock might have
688             * been set to -1 by the close command */
689            if ((int) res_kevents[i].ident == oldsock) {
690                int s;
691
692                /* handle error conditions */
693                if ((res_kevents[i].flags & (EV_ERROR | EV_EOF)) > 0) {
694                    if (cleanuping) {
695                        break;
696                    }
697                    Tcl_ResetResult(in);
698                    Tcl_SetResult(in, "control socket closed", NULL);
699                    close(kq);
700                    return TCL_ERROR;
701                }
702
703                /* else: new connection attempt(s) */
704                for (;;) {
705                    if (-1 == (s = accept(sock, NULL, NULL))) {
706                        if (cleanuping) {
707                            break;
708                        }
709                        if (errno == EWOULDBLOCK) {
710                            break;
711                        }
712                        Tcl_SetErrno(errno);
713                        Tcl_ResetResult(in);
714                        Tcl_AppendResult(in, "accept: ", (char *) Tcl_PosixError(in), NULL);
715                        close(kq);
716                        return TCL_ERROR;
717                    }
718
719                    flags = fcntl(s, F_GETFL, 0);
720                    if (-1 == fcntl(s, F_SETFL, flags & ~O_NONBLOCK)) {
721                        ui_warn("tracelib: couldn't mark socket as blocking");
722                        close(s);
723                        continue;
724                    }
725
726                    /* register the new socket in the kqueue */
727                    EV_SET(&kev, s, EVFILT_READ, EV_ADD | EV_RECEIPT, 0, 0, NULL);
728                    if (1 != kevent(kq, &kev, 1, &kev, 1, NULL)) {
729                        ui_warn("tracelib: error adding socket to kqueue");
730                        close(s);
731                        continue;
732                    }
733                    /* kevent(2) on EV_RECEIPT: When passed as input, it forces EV_ERROR to
734                     * always be returned. When a filter is successfully added, the data field
735                     * will be zero. */
736                    if ((kev.flags & EV_ERROR) == 0 || ((kev.flags & EV_ERROR) > 0 && kev.data != 0)) {
737                        ui_warn("tracelib: error adding socket to kqueue");
738                        close(s);
739                        continue;
740                    }
741
742                    opensockcount++;
743                }
744
745                if (cleanuping) {
746                    break;
747                }
748            } else {
749                /* if the socket is to be closed, or */
750                if ((res_kevents[i].flags & (EV_EOF | EV_ERROR)) > 0
751                    /* new data is available, and its processing tells us to
752                     * close the socket */
753                    || (!process_line(res_kevents[i].ident))) {
754                    /* an error occured or process_line suggested closing this
755                     * socket */
756                    close(res_kevents[i].ident);
757                    /* closing the socket will automatically remove it from the
758                     * kqueue :) */
759                    opensockcount--;
760                }
761            }
762        }
763    }
764
765    /* NOTE: We aren't necessarily closing all client sockets here! */
766    if (opensockcount > 0) {
767        fprintf(stderr, "tracelib: %d open sockets will leak at end of runcmd\n", opensockcount);
768    }
769    pthread_mutex_lock(&sock_mutex);
770    close(kq);
771    kq = -1;
772    pthread_mutex_unlock(&sock_mutex);
773
774    return TCL_OK;
775}
776
777static int TracelibCleanCmd(Tcl_Interp *interp UNUSED) {
778#define safe_free(x) do{free(x); x=0;}while(0);
779    cleanuping = 1;
780    pthread_mutex_lock(&sock_mutex);
781    if (sock != -1) {
782        /* shutdown(sock, SHUT_RDWR);*/
783        close(sock);
784        sock = -1;
785    }
786    pthread_mutex_unlock(&sock_mutex);
787    if (name) {
788        unlink(name);
789        safe_free(name);
790    }
791    if (filemap) {
792        safe_free(filemap);
793    }
794    if (depends) {
795        safe_free(depends);
796    }
797    enable_fence = 0;
798#undef safe_free
799    return TCL_OK;
800}
801
802static int TracelibCloseSocketCmd(Tcl_Interp *interp UNUSED) {
803    /* interp might be UNUSED on systems without EVFILT_USER */
804    cleanuping = 1;
805    pthread_mutex_lock(&sock_mutex);
806    if (sock != -1) {
807        int oldsock = sock;
808        /*shutdown(sock, SHUT_RDWR);*/
809        close(oldsock);
810        sock = -1;
811
812        if (kq != -1) {
813#           ifdef EVFILT_USER
814            int ret;
815            struct kevent kev;
816            EV_SET(&kev, oldsock, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
817            if (-1 == (ret = kevent(kq, &kev, 1, NULL, 0, NULL))) {
818                Tcl_SetErrno(errno);
819                Tcl_ResetResult(interp);
820                Tcl_AppendResult(interp, "kevent: ", (char *) Tcl_PosixError(interp), NULL);
821                pthread_mutex_unlock(&sock_mutex);
822                return TCL_ERROR;
823            }
824#           else /* ifdef EVFILT_USER */
825            /* We know the pipes have been created because kq != -1 and we have
826             * the lock. We don't have to check for errors, because none should
827             * occur but when the pipe is full, which we wouldn't care about.
828             * */
829            write(selfpipe[1], "!", 1);
830#           endif /* ifdef EVFILT_USER */
831        }
832    }
833    pthread_mutex_unlock(&sock_mutex);
834    return TCL_OK;
835}
836
837static int TracelibSetDeps(Tcl_Interp *interp UNUSED, int objc, Tcl_Obj *CONST objv[]) {
838    char *t, * d;
839    size_t l;
840    if (objc != 3) {
841        Tcl_WrongNumArgs(interp, 2, objv, "number of arguments should be exactly 3");
842        return TCL_ERROR;
843    }
844
845    d = Tcl_GetString(objv[2]);
846    l = strlen(d);
847    depends = malloc(l + 2);
848    if (!depends) {
849        Tcl_SetResult(interp, "memory allocation failed", TCL_STATIC);
850        return TCL_ERROR;
851    }
852    depends[l + 1] = 0;
853    strlcpy(depends, d, l + 2);
854    for (t = depends; *t; ++t)
855        if (*t == ' ') {
856            *t++ = 0;
857        }
858
859    return TCL_OK;
860}
861
862static int TracelibEnableFence(Tcl_Interp *interp UNUSED) {
863    enable_fence = 1;
864    if (filemap) {
865        free(filemap);
866    }
867    filemap = 0;
868    return TCL_OK;
869}
870
871int TracelibCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
872    int result = TCL_OK;
873    static const char *options[] = {"setname", "opensocket", "run", "clean", "setsandbox", "closesocket", "setdeps", "enablefence", 0};
874    typedef enum {
875        kSetName,
876        kOpenSocket,
877        kRun,
878        kClean,
879        kSetSandbox,
880        kCloseSocket,
881        kSetDeps,
882        kEnableFence
883    } EOptions;
884    EOptions current_option;
885
886    /* There is no args for commands now. */
887    if (objc < 2) {
888        Tcl_WrongNumArgs(interp, 1, objv, "option");
889        return TCL_ERROR;
890    }
891
892    result = Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0, (int *)&current_option);
893    if (result == TCL_OK) {
894        switch (current_option) {
895            case kSetName:
896                result = TracelibSetNameCmd(interp, objc, objv);
897                break;
898            case kOpenSocket:
899                result = TracelibOpenSocketCmd(interp);
900                break;
901            case kRun:
902                result = TracelibRunCmd(interp);
903                break;
904            case kClean:
905                result = TracelibCleanCmd(interp);
906                break;
907            case kCloseSocket:
908                result = TracelibCloseSocketCmd(interp);
909                break;
910            case kSetSandbox:
911                result = TracelibSetSandboxCmd(interp, objc, objv);
912                break;
913            case kSetDeps:
914                result = TracelibSetDeps(interp, objc, objv);
915                break;
916            case kEnableFence:
917                result = TracelibEnableFence(interp);
918                break;
919        }
920    }
921
922    return result;
923}
Note: See TracBrowser for help on using the repository browser.