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