1/*++
2/* NAME
3/*	unix_pass_trigger 3
4/* SUMMARY
5/*	wakeup UNIX-domain file descriptor listener
6/* SYNOPSIS
7/*	#include <trigger.h>
8/*
9/*	int	unix_pass_trigger(service, buf, len, timeout)
10/*	const char *service;
11/*	const char *buf;
12/*	ssize_t	len;
13/*	int	timeout;
14/* DESCRIPTION
15/*	unix_pass_trigger() wakes up the named UNIX-domain server by sending
16/*	a brief connection to it and writing the named buffer.
17/*
18/*	The connection is closed by a background thread. Some kernels
19/*	cannot handle client-side disconnect before the server has
20/*	received the message.
21/*
22/*	Arguments:
23/* .IP service
24/*	Name of the communication endpoint.
25/* .IP buf
26/*	Address of data to be written.
27/* .IP len
28/*	Amount of data to be written.
29/* .IP timeout
30/*	Deadline in seconds. Specify a value <= 0 to disable
31/*	the time limit.
32/* DIAGNOSTICS
33/*	The result is zero in case of success, -1 in case of problems.
34/* SEE ALSO
35/*	unix_pass_connect(3), UNIX-domain client
36/* LICENSE
37/* .ad
38/* .fi
39/*	The Secure Mailer license must be distributed with this software.
40/* AUTHOR(S)
41/*	Wietse Venema
42/*	IBM T.J. Watson Research
43/*	P.O. Box 704
44/*	Yorktown Heights, NY 10598, USA
45/*--*/
46
47/* System library. */
48
49#include <sys_defs.h>
50#include <sys/socket.h>
51#include <unistd.h>
52#include <string.h>
53
54/* Utility library. */
55
56#include <msg.h>
57#include <connect.h>
58#include <iostuff.h>
59#include <mymalloc.h>
60#include <events.h>
61#include <trigger.h>
62
63struct unix_pass_trigger {
64    int     fd;
65    char   *service;
66    int    *pair;
67};
68
69/* unix_pass_trigger_event - disconnect from peer */
70
71static void unix_pass_trigger_event(int event, char *context)
72{
73    struct unix_pass_trigger *up = (struct unix_pass_trigger *) context;
74    static const char *myname = "unix_pass_trigger_event";
75
76    /*
77     * Disconnect.
78     */
79    if (event == EVENT_TIME)
80	msg_warn("%s: read timeout for service %s", myname, up->service);
81    event_disable_readwrite(up->fd);
82    event_cancel_timer(unix_pass_trigger_event, context);
83    /* Don't combine multiple close() calls into one boolean expression. */
84    if (close(up->fd) < 0)
85	msg_warn("%s: close %s: %m", myname, up->service);
86    if (close(up->pair[0]) < 0)
87	msg_warn("%s: close pipe: %m", myname);
88    if (close(up->pair[1]) < 0)
89	msg_warn("%s: close pipe: %m", myname);
90    myfree(up->service);
91    myfree((char *) up);
92}
93
94/* unix_pass_trigger - wakeup UNIX-domain server */
95
96int     unix_pass_trigger(const char *service, const char *buf, ssize_t len, int timeout)
97{
98    const char *myname = "unix_pass_trigger";
99    int     pair[2];
100    struct unix_pass_trigger *up;
101    int     fd;
102
103    if (msg_verbose > 1)
104	msg_info("%s: service %s", myname, service);
105
106    /*
107     * Connect...
108     */
109    if ((fd = unix_pass_connect(service, BLOCKING, timeout)) < 0) {
110	if (msg_verbose)
111	    msg_warn("%s: connect to %s: %m", myname, service);
112	return (-1);
113    }
114    close_on_exec(fd, CLOSE_ON_EXEC);
115
116    /*
117     * Create a pipe, and send one pipe end to the server.
118     */
119    if (pipe(pair) < 0)
120	msg_fatal("%s: pipe: %m", myname);
121    close_on_exec(pair[0], CLOSE_ON_EXEC);
122    close_on_exec(pair[1], CLOSE_ON_EXEC);
123    if (unix_send_fd(fd, pair[0]) < 0)
124	msg_fatal("%s: send file descriptor: %m", myname);
125
126    /*
127     * Stash away context.
128     */
129    up = (struct unix_pass_trigger *) mymalloc(sizeof(*up));
130    up->fd = fd;
131    up->service = mystrdup(service);
132    up->pair = pair;
133
134    /*
135     * Write the request...
136     */
137    if (write_buf(pair[1], buf, len, timeout) < 0
138	|| write_buf(pair[1], "", 1, timeout) < 0)
139	if (msg_verbose)
140	    msg_warn("%s: write to %s: %m", myname, service);
141
142    /*
143     * Wakeup when the peer disconnects, or when we lose patience.
144     */
145    if (timeout > 0)
146	event_request_timer(unix_pass_trigger_event, (char *) up, timeout + 100);
147    event_enable_read(fd, unix_pass_trigger_event, (char *) up);
148    return (0);
149}
150