Libinfo-173.tar.gz
[apple/libinfo.git] / netinfo.subproj / multi_call.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 * multi_call: send out multiple call messages, wait for first reply
27 * Copyright (C) 1991 by NeXT, Inc.
28 */
29 #include <rpc/rpc.h>
30 #include <rpc/pmap_prot.h>
31 #include <sys/socket.h>
32 #include <syslog.h>
33 #include <sys/param.h>
34 #include <arpa/inet.h>
35 #include <string.h>
36 #include <unistd.h>
37
38 #ifdef NETINFOD
39 # include "socket_lock.h"
40 # include "clib.h"
41 # define multi_call _multi_call
42 #else
43 # define socket_lock()
44 # define socket_unlock()
45 # include "clib.h"
46 #endif
47
48
49 #define NRETRIES 5
50 #define USECS_PER_SEC 1000000
51
52
53 /*
54 * Wrapper for gethostname() syscall
55 */
56 static char *
57 get_hostname(void)
58 {
59 int len;
60 static char hostname[MAXHOSTNAMELEN + 1];
61
62 len = gethostname(hostname, sizeof(hostname));
63 if (len < 0) {
64 hostname[0] = 0;
65 } else {
66 hostname[len] = 0;
67 }
68 return (hostname);
69 }
70
71
72 /*
73 * Encode a call message
74 */
75 static int
76 encodemsg(
77 char *buf,
78 int buflen,
79 struct rpc_msg *call,
80 unsigned prognum,
81 unsigned versnum,
82 unsigned procnum,
83 xdrproc_t xdr_args,
84 void *arg
85 )
86 {
87 XDR xdr;
88 unsigned size;
89 unsigned pos;
90
91 xdrmem_create(&xdr, buf, buflen, XDR_ENCODE);
92 if (!xdr_callmsg(&xdr, call) ||
93 !xdr_u_int(&xdr, &prognum) ||
94 !xdr_u_int(&xdr, &versnum) ||
95 !xdr_u_int(&xdr, &procnum)) {
96 return (0);
97 }
98 pos = xdr_getpos(&xdr);
99 xdr_setpos(&xdr, pos + BYTES_PER_XDR_UNIT);
100 if (!(*xdr_args)(&xdr, arg)) {
101 return (0);
102 }
103 size = xdr_getpos(&xdr) - pos;
104 xdr_setpos(&xdr, pos);
105 if (!xdr_u_int(&xdr, &size)) {
106 return (0);
107 }
108 return (pos + BYTES_PER_XDR_UNIT + size);
109 }
110
111 /*
112 * Decode a reply message
113 */
114 static int
115 decodemsg(
116 XDR *xdr,
117 xdrproc_t xdr_res,
118 void *res
119 )
120 {
121 unsigned port;
122 unsigned len;
123 long *buf;
124 XDR bufxdr;
125
126 if (!xdr_u_int(xdr, &port) ||
127 !xdr_u_int(xdr, &len) ||
128 !(buf = (long *)xdr_inline(xdr, len))) {
129 return (0);
130 }
131 xdrmem_create(&bufxdr, (char *)buf, len * BYTES_PER_XDR_UNIT,
132 XDR_DECODE);
133 if (!(*xdr_res)(&bufxdr, res)) {
134 return (0);
135 }
136 return (1);
137
138 }
139
140 /*
141 * Do the real work
142 */
143 enum clnt_stat
144 multi_call(
145 unsigned naddrs,
146 struct in_addr *addrs,
147 u_long prognum,
148 u_long versnum,
149 u_long procnum,
150 xdrproc_t xdr_args,
151 void *argsvec,
152 unsigned argsize,
153 xdrproc_t xdr_res,
154 void *res,
155 int (*eachresult)(void *, struct sockaddr_in *, int),
156 int timeout
157 )
158 {
159 struct authunix_parms aup;
160 char credbuf[MAX_AUTH_BYTES];
161 struct opaque_auth cred;
162 struct opaque_auth verf;
163 int gids[NGROUPS];
164 int s;
165 struct timeval tv;
166 struct timeval subtimeout;
167 unsigned long long utimeout;
168 int callno;
169 int serverno;
170 struct rpc_msg call;
171 struct rpc_msg reply;
172 struct sockaddr_in sin;
173 struct sockaddr_in from;
174 int fromsize;
175 char buf[UDPMSGSIZE];
176 int buflen;
177 unsigned trans_id;
178 int dtablesize = getdtablesize();
179 XDR xdr;
180 int sendlen;
181 fd_set fds;
182
183 /*
184 * Fill in Unix auth stuff
185 */
186 aup.aup_time = time(0);
187 aup.aup_machname = get_hostname();
188 aup.aup_uid = getuid();
189 aup.aup_gid = getgid();
190 aup.aup_gids = gids;
191 aup.aup_len = getgroups(NGROUPS, aup.aup_gids);
192
193 /*
194 * Encode unix auth
195 */
196 xdrmem_create(&xdr, credbuf, sizeof(credbuf), XDR_ENCODE);
197 if (!xdr_authunix_parms(&xdr, &aup)) {
198 return (RPC_CANTENCODEARGS);
199 }
200 cred.oa_flavor = AUTH_UNIX;
201 cred.oa_base = credbuf;
202 cred.oa_length = xdr_getpos(&xdr);
203
204 verf.oa_flavor = AUTH_NULL;
205 verf.oa_length = 0;
206
207 /*
208 * Set up call header information
209 */
210 trans_id = time(0) ^ getpid();
211 call.rm_xid = trans_id;
212 call.rm_direction = CALL;
213 call.rm_call.cb_rpcvers = 2;
214 call.rm_call.cb_prog = PMAPPROG;
215 call.rm_call.cb_vers = PMAPVERS;
216 call.rm_call.cb_proc = PMAPPROC_CALLIT;
217 call.rm_call.cb_cred = cred;
218 call.rm_call.cb_verf = verf;
219
220
221 /*
222 * Open socket
223 */
224 socket_lock();
225 s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
226 socket_unlock();
227 if (s < 0) {
228 syslog(LOG_ERR, "multi_call: socket: %m");
229 return (RPC_FAILED);
230 }
231
232 /*
233 * Init timeouts
234 */
235 utimeout = ((unsigned long long) timeout) * USECS_PER_SEC;
236 subtimeout.tv_sec = (utimeout >> NRETRIES) / USECS_PER_SEC;
237 subtimeout.tv_usec = (utimeout >> NRETRIES) % USECS_PER_SEC;
238 tv = subtimeout;
239
240 /*
241 * Init address info
242 */
243 sin.sin_family = AF_INET;
244 sin.sin_port = htons(PMAPPORT);
245 bzero(sin.sin_zero, sizeof(sin.sin_zero));
246
247 for (callno = 0; callno <= NRETRIES; callno++) {
248 /*
249 * Send a call message to each host with the appropriate args
250 */
251 for (serverno = 0; serverno < naddrs; serverno++) {
252 call.rm_xid = trans_id + serverno;
253 buflen = encodemsg(buf, sizeof(buf), &call,
254 prognum, versnum, procnum,
255 xdr_args, (argsvec +
256 (serverno * argsize)));
257 if (buflen == 0) {
258 /*
259 * Encode failed
260 */
261 continue;
262 }
263 sin.sin_addr = addrs[serverno];
264 sendlen = sendto(s, buf, buflen, 0,
265 (struct sockaddr *)&sin, sizeof(sin));
266 if (sendlen != buflen) {
267 syslog(LOG_ERR,
268 "Cannot send multicall packet to %s: %m",
269 inet_ntoa(addrs[serverno]));
270 }
271 }
272
273 /*
274 * Double the timeout from previous timeout, if necessary
275 */
276 if (callno > 1) {
277 tv.tv_sec *= 2;
278 tv.tv_usec *= 2;
279 if (tv.tv_usec >= USECS_PER_SEC) {
280 tv.tv_usec -= USECS_PER_SEC;
281 tv.tv_sec++;
282 }
283 }
284
285
286 #ifdef NETINFOD
287 /*
288 * Check for cancel by user
289 */
290 if (alert_aborted()) {
291 socket_lock();
292 close(s);
293 socket_unlock();
294 return (RPC_FAILED);
295 }
296 #endif
297 /*
298 * Wait for reply
299 */
300 FD_ZERO(&fds);
301 FD_SET(s, &fds);
302 switch (select(dtablesize, &fds, NULL, NULL, &tv)) {
303 case -1:
304 syslog(LOG_ERR, "select failure: %m");
305 continue;
306 case 0:
307 continue;
308 default:
309 break;
310 }
311
312 /*
313 * Receive packet
314 */
315 fromsize = sizeof(from);
316 buflen = recvfrom(s, buf, sizeof(buf), 0,
317 (struct sockaddr *)&from, &fromsize);
318 if (buflen < 0) {
319 continue;
320 }
321
322 /*
323 * Decode packet and if no errors, call eachresult
324 */
325 xdrmem_create(&xdr, buf, buflen, XDR_DECODE);
326 reply.rm_reply.rp_acpt.ar_results.proc = xdr_void;
327 reply.rm_reply.rp_acpt.ar_results.where = NULL;
328 if (xdr_replymsg(&xdr, &reply) &&
329 (reply.rm_xid >= trans_id) &&
330 (reply.rm_xid < trans_id + naddrs) &&
331 (reply.rm_reply.rp_stat == MSG_ACCEPTED) &&
332 (reply.acpted_rply.ar_stat == SUCCESS) &&
333 decodemsg(&xdr, xdr_res, res)) {
334 if ((*eachresult)(res, &from,
335 reply.rm_xid - trans_id)) {
336 xdr_free(xdr_res, res);
337 socket_lock();
338 close(s);
339 socket_unlock();
340 return (RPC_SUCCESS);
341 }
342 }
343 xdr_free(xdr_res, res);
344 }
345 socket_lock();
346 close(s);
347 socket_unlock();
348 return (RPC_TIMEDOUT);
349 }
350
351
352
353