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