procs.c revision 99790
1/* 2 * Copyright (c) 1995 3 * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed for the FreeBSD project 16 * 4. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 */ 33 34#ifndef lint 35static const char rcsid[] = 36 "$FreeBSD: head/usr.sbin/rpc.statd/procs.c 99790 2002-07-11 17:36:09Z alfred $"; 37#endif /* not lint */ 38 39#include <errno.h> 40#include <stdio.h> 41#include <stdlib.h> 42#include <string.h> 43#include <unistd.h> 44#include <rpc/rpc.h> 45#include <syslog.h> 46#include <vis.h> 47#include <netdb.h> /* for gethostbyname() */ 48#include <sys/types.h> 49#include <sys/socket.h> 50#include <netinet/in.h> 51#include <arpa/inet.h> 52 53#include "statd.h" 54 55/* sm_check_hostname -------------------------------------------------------- */ 56/* 57 * Purpose: Check `mon_name' member of sm_name struct to ensure that the array 58 * consists only of printable characters. 59 * 60 * Returns: TRUE if hostname is good. FALSE if hostname contains binary or 61 * otherwise non-printable characters. 62 * 63 * Notes: Will syslog(3) to warn of corrupt hostname. 64 */ 65 66int sm_check_hostname(struct svc_req *req, char *arg) 67{ 68 int len, dstlen, ret; 69 struct sockaddr_in *claddr; 70 char *dst; 71 72 len = strlen(arg); 73 dstlen = (4 * len) + 1; 74 dst = malloc(dstlen); 75 claddr = svc_getcaller(req->rq_xprt); 76 ret = 1; 77 78 if (claddr == NULL || dst == NULL) 79 { 80 ret = 0; 81 } 82 else if (strvis(dst, arg, VIS_WHITE) != len) 83 { 84 syslog(LOG_ERR, 85 "sm_stat: client %s hostname %s contained invalid characters.", 86 inet_ntoa(claddr->sin_addr), 87 dst); 88 ret = 0; 89 } 90 free(dst); 91 return (ret); 92} 93 94/* sm_stat_1 --------------------------------------------------------------- */ 95/* 96 Purpose: RPC call to enquire if a host can be monitored 97 Returns: TRUE for any hostname that can be looked up to give 98 an address. 99*/ 100 101struct sm_stat_res *sm_stat_1_svc(sm_name *arg, struct svc_req *req) 102{ 103 static sm_stat_res res; 104 static int err; 105 106 err = 1; 107 if ((err = sm_check_hostname(req, arg->mon_name)) == 0) 108 { 109 res.res_stat = stat_fail; 110 } 111 if (err != 0) 112 { 113 if (debug) 114 syslog(LOG_DEBUG, "stat called for host %s", arg->mon_name); 115 if (gethostbyname(arg->mon_name)) 116 res.res_stat = stat_succ; 117 else 118 { 119 syslog(LOG_ERR, "invalid hostname to sm_stat: %s", arg->mon_name); 120 res.res_stat = stat_fail; 121 } 122 } 123 res.state = status_info->ourState; 124 return (&res); 125} 126 127/* sm_mon_1 ---------------------------------------------------------------- */ 128/* 129 Purpose: RPC procedure to establish a monitor request 130 Returns: Success, unless lack of resources prevents 131 the necessary structures from being set up 132 to record the request, or if the hostname is not 133 valid (as judged by gethostbyname()) 134*/ 135 136struct sm_stat_res *sm_mon_1_svc(mon *arg, struct svc_req *req) 137{ 138 static sm_stat_res res; 139 HostInfo *hp; 140 static int err; 141 MonList *lp; 142 143 if ((err = sm_check_hostname(req, arg->mon_id.mon_name)) == 0) 144 { 145 res.res_stat = stat_fail; 146 } 147 148 if (err != 0) 149 { 150 if (debug) 151 { 152 syslog(LOG_DEBUG, "monitor request for host %s", arg->mon_id.mon_name); 153 syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d", 154 arg->mon_id.mon_name, 155 arg->mon_id.my_id.my_prog, arg->mon_id.my_id.my_vers, 156 arg->mon_id.my_id.my_proc); 157 } 158 res.res_stat = stat_fail; /* Assume fail until set otherwise */ 159 res.state = status_info->ourState; 160 161 /* Find existing host entry, or create one if not found */ 162 /* If find_host() fails, it will have logged the error already. */ 163 if (!gethostbyname(arg->mon_id.mon_name)) 164 { 165 syslog(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name); 166 } 167 else if ((hp = find_host(arg->mon_id.mon_name, TRUE))) 168 { 169 lp = (MonList *)malloc(sizeof(MonList)); 170 if (!lp) 171 { 172 syslog(LOG_ERR, "Out of memory"); 173 } 174 else 175 { 176 strncpy(lp->notifyHost, arg->mon_id.my_id.my_name, SM_MAXSTRLEN); 177 lp->notifyProg = arg->mon_id.my_id.my_prog; 178 lp->notifyVers = arg->mon_id.my_id.my_vers; 179 lp->notifyProc = arg->mon_id.my_id.my_proc; 180 memcpy(lp->notifyData, arg->priv, sizeof(lp->notifyData)); 181 182 lp->next = hp->monList; 183 hp->monList = lp; 184 sync_file(); 185 186 res.res_stat = stat_succ; /* Report success */ 187 } 188 } 189 } 190 return (&res); 191} 192 193/* do_unmon ---------------------------------------------------------------- */ 194/* 195 Purpose: Remove a monitor request from a host 196 Returns: TRUE if found, FALSE if not found. 197 Notes: Common code from sm_unmon_1_svc and sm_unmon_all_1_svc 198 In the unlikely event of more than one identical monitor 199 request, all are removed. 200*/ 201 202static int do_unmon(HostInfo *hp, my_id *idp) 203{ 204 MonList *lp, *next; 205 MonList *last = NULL; 206 int result = FALSE; 207 208 lp = hp->monList; 209 while (lp) 210 { 211 if (!strncasecmp(idp->my_name, lp->notifyHost, SM_MAXSTRLEN) 212 && (idp->my_prog == lp->notifyProg) && (idp->my_proc == lp->notifyProc) 213 && (idp->my_vers == lp->notifyVers)) 214 { 215 /* found one. Unhook from chain and free. */ 216 next = lp->next; 217 if (last) last->next = next; 218 else hp->monList = next; 219 free(lp); 220 lp = next; 221 result = TRUE; 222 } 223 else 224 { 225 last = lp; 226 lp = lp->next; 227 } 228 } 229 return (result); 230} 231 232/* sm_unmon_1 -------------------------------------------------------------- */ 233/* 234 Purpose: RPC procedure to release a monitor request. 235 Returns: Local machine's status number 236 Notes: The supplied mon_id should match the value passed in an 237 earlier call to sm_mon_1 238*/ 239 240struct sm_stat *sm_unmon_1_svc(mon_id *arg, struct svc_req *req) 241{ 242 static sm_stat res; 243 HostInfo *hp; 244 245 if (debug) 246 { 247 syslog(LOG_DEBUG, "un-monitor request for host %s", arg->mon_name); 248 syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d", 249 arg->mon_name, 250 arg->my_id.my_prog, arg->my_id.my_vers, arg->my_id.my_proc); 251 } 252 253 if ((hp = find_host(arg->mon_name, FALSE))) 254 { 255 if (do_unmon(hp, &arg->my_id)) sync_file(); 256 else 257 { 258 syslog(LOG_ERR, "unmon request from %s, no matching monitor", 259 arg->my_id.my_name); 260 } 261 } 262 else syslog(LOG_ERR, "unmon request from %s for unknown host %s", 263 arg->my_id.my_name, arg->mon_name); 264 265 res.state = status_info->ourState; 266 267 return (&res); 268} 269 270/* sm_unmon_all_1 ---------------------------------------------------------- */ 271/* 272 Purpose: RPC procedure to release monitor requests. 273 Returns: Local machine's status number 274 Notes: Releases all monitor requests (if any) from the specified 275 host and program number. 276*/ 277 278struct sm_stat *sm_unmon_all_1_svc(my_id *arg, struct svc_req *req) 279{ 280 static sm_stat res; 281 HostInfo *hp; 282 int i; 283 284 if (debug) 285 { 286 syslog(LOG_DEBUG, "unmon_all for host: %s prog: %d ver: %d proc: %d", 287 arg->my_name, arg->my_prog, arg->my_vers, arg->my_proc); 288 } 289 290 for (i = status_info->noOfHosts, hp = status_info->hosts; i; i--, hp++) 291 { 292 do_unmon(hp, arg); 293 } 294 sync_file(); 295 296 res.state = status_info->ourState; 297 298 return (&res); 299} 300 301/* sm_simu_crash_1 --------------------------------------------------------- */ 302/* 303 Purpose: RPC procedure to simulate a crash 304 Returns: Nothing 305 Notes: Standardised mechanism for debug purposes 306 The specification says that we should drop all of our 307 status information (apart from the list of monitored hosts 308 on disc). However, this would confuse the rpc.lockd 309 which would be unaware that all of its monitor requests 310 had been silently junked. Hence we in fact retain all 311 current requests and simply increment the status counter 312 and inform all hosts on the monitor list. 313*/ 314 315void *sm_simu_crash_1_svc(void *v, struct svc_req *req) 316{ 317 static char dummy; 318 int work_to_do; 319 HostInfo *hp; 320 int i; 321 322 if (debug) syslog(LOG_DEBUG, "simu_crash called!!"); 323 324 /* Simulate crash by setting notify-required flag on all monitored */ 325 /* hosts, and incrementing our status number. notify_hosts() is */ 326 /* then called to fork a process to do the notifications. */ 327 328 for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++) 329 { 330 if (hp->monList) 331 { 332 work_to_do = TRUE; 333 hp->notifyReqd = TRUE; 334 } 335 } 336 status_info->ourState += 2; /* always even numbers if not crashed */ 337 338 if (work_to_do) notify_hosts(); 339 340 return (&dummy); 341} 342 343/* sm_notify_1 ------------------------------------------------------------- */ 344/* 345 Purpose: RPC procedure notifying local statd of the crash of another 346 Returns: Nothing 347 Notes: There is danger of deadlock, since it is quite likely that 348 the client procedure that we call will in turn call us 349 to remove or adjust the monitor request. 350 We therefore fork() a process to do the notifications. 351 Note that the main HostInfo structure is in a mmap() 352 region and so will be shared with the child, but the 353 monList pointed to by the HostInfo is in normal memory. 354 Hence if we read the monList before forking, we are 355 protected from the parent servicing other requests 356 that modify the list. 357*/ 358 359void *sm_notify_1_svc(stat_chge *arg, struct svc_req *req) 360{ 361 struct timeval timeout = { 20, 0 }; /* 20 secs timeout */ 362 CLIENT *cli; 363 static char dummy; 364 sm_status tx_arg; /* arg sent to callback procedure */ 365 MonList *lp; 366 HostInfo *hp; 367 pid_t pid; 368 369 if (debug) syslog(LOG_DEBUG, "notify from host %s, new state %d", 370 arg->mon_name, arg->state); 371 372 hp = find_host(arg->mon_name, FALSE); 373 if (!hp) 374 { 375 /* Never heard of this host - why is it notifying us? */ 376 syslog(LOG_ERR, "Unsolicited notification from host %s", arg->mon_name); 377 return; 378 } 379 lp = hp->monList; 380 if (!lp) return (FALSE); /* We know this host, but have no */ 381 /* outstanding requests. */ 382 pid = fork(); 383 if (pid == -1) 384 { 385 syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno)); 386 return; 387 } 388 if (pid) return (&dummy); /* Parent returns */ 389 390 while (lp) 391 { 392 tx_arg.mon_name = arg->mon_name; 393 tx_arg.state = arg->state; 394 memcpy(tx_arg.priv, lp->notifyData, sizeof(tx_arg.priv)); 395 cli = clnt_create(lp->notifyHost, lp->notifyProg, lp->notifyVers, "udp"); 396 if (!cli) 397 { 398 syslog(LOG_ERR, "Failed to contact host %s%s", lp->notifyHost, 399 clnt_spcreateerror("")); 400 } 401 else 402 { 403 if (clnt_call(cli, lp->notifyProc, xdr_sm_status, &tx_arg, xdr_void, 404 &dummy, timeout) != RPC_SUCCESS) 405 { 406 syslog(LOG_ERR, "Failed to call rpc.statd client at host %s", 407 lp->notifyHost); 408 } 409 clnt_destroy(cli); 410 } 411 lp = lp->next; 412 } 413 414 exit (0); /* Child quits */ 415} 416