procs.c revision 99783
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 99783 2002-07-11 17:11:42Z 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 49#include "statd.h" 50 51/* sm_check_hostname -------------------------------------------------------- */ 52/* 53 * Purpose: Check `mon_name' member of sm_name struct to ensure that the array 54 * consists only of printable characters. 55 * 56 * Returns: TRUE if hostname is good. FALSE if hostname contains binary or 57 * otherwise non-printable characters. 58 * 59 * Notes: Will syslog(3) to warn of corrupt hostname. 60 */ 61 62int sm_check_hostname(struct svc_req *req, char *arg) 63{ 64 int len, dstlen, ret; 65 struct sockaddr_in *claddr; 66 char *dst; 67 68 len = strlen(arg); 69 dstlen = (4 * len) + 1; 70 dst = malloc(dstlen); 71 claddr = svc_getcaller(req->rq_xprt); 72 ret = 1; 73 74 if (claddr == NULL || dst == NULL) 75 { 76 ret = 0; 77 } 78 else if (strvis(dst, arg, VIS_WHITE) != len) 79 { 80 syslog(LOG_ERR, 81 "sm_stat: client %s hostname %s contained invalid characters.", 82 inet_ntoa(claddr->sin_addr), 83 dst); 84 ret = 0; 85 } 86 free(dst); 87 return (ret); 88} 89 90/* sm_stat_1 --------------------------------------------------------------- */ 91/* 92 Purpose: RPC call to enquire if a host can be monitored 93 Returns: TRUE for any hostname that can be looked up to give 94 an address. 95*/ 96 97struct sm_stat_res *sm_stat_1_svc(sm_name *arg, struct svc_req *req) 98{ 99 static sm_stat_res res; 100 static int err; 101 102 err = 1; 103 if ((err = sm_check_hostname(req, arg->mon_name)) == 0) 104 { 105 res.res_stat = stat_fail; 106 } 107 if (err != 0) 108 { 109 if (debug) 110 syslog(LOG_DEBUG, "stat called for host %s", arg->mon_name); 111 if (gethostbyname(arg->mon_name)) 112 res.res_stat = stat_succ; 113 else 114 { 115 syslog(LOG_ERR, "invalid hostname to sm_stat: %s", arg->mon_name); 116 res.res_stat = stat_fail; 117 } 118 } 119 res.state = status_info->ourState; 120 return (&res); 121} 122 123/* sm_mon_1 ---------------------------------------------------------------- */ 124/* 125 Purpose: RPC procedure to establish a monitor request 126 Returns: Success, unless lack of resources prevents 127 the necessary structures from being set up 128 to record the request, or if the hostname is not 129 valid (as judged by gethostbyname()) 130*/ 131 132struct sm_stat_res *sm_mon_1_svc(mon *arg, struct svc_req *req) 133{ 134 static sm_stat_res res; 135 HostInfo *hp; 136 static int err; 137 MonList *lp; 138 139 if ((err = sm_check_hostname(req, arg->mon_id.mon_name)) == 0) 140 { 141 res.res_stat = stat_fail; 142 } 143 144 if (err != 0) 145 { 146 if (debug) 147 { 148 syslog(LOG_DEBUG, "monitor request for host %s", arg->mon_id.mon_name); 149 syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d", 150 arg->mon_id.mon_name, arg->mon_id.my_id.my_name, 151 arg->mon_id.my_id.my_prog, arg->mon_id.my_id.my_vers, 152 arg->mon_id.my_id.my_proc); 153 } 154 res.res_stat = stat_fail; /* Assume fail until set otherwise */ 155 res.state = status_info->ourState; 156 157 /* Find existing host entry, or create one if not found */ 158 /* If find_host() fails, it will have logged the error already. */ 159 if (!gethostbyname(arg->mon_id.mon_name)) 160 { 161 syslog(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name); 162 } 163 else if ((hp = find_host(arg->mon_id.mon_name, TRUE))) 164 { 165 lp = (MonList *)malloc(sizeof(MonList)); 166 if (!lp) 167 { 168 syslog(LOG_ERR, "Out of memory"); 169 } 170 else 171 { 172 strncpy(lp->notifyHost, arg->mon_id.my_id.my_name, SM_MAXSTRLEN); 173 lp->notifyProg = arg->mon_id.my_id.my_prog; 174 lp->notifyVers = arg->mon_id.my_id.my_vers; 175 lp->notifyProc = arg->mon_id.my_id.my_proc; 176 memcpy(lp->notifyData, arg->priv, sizeof(lp->notifyData)); 177 178 lp->next = hp->monList; 179 hp->monList = lp; 180 sync_file(); 181 182 res.res_stat = stat_succ; /* Report success */ 183 } 184 } 185 } 186 return (&res); 187} 188 189/* do_unmon ---------------------------------------------------------------- */ 190/* 191 Purpose: Remove a monitor request from a host 192 Returns: TRUE if found, FALSE if not found. 193 Notes: Common code from sm_unmon_1_svc and sm_unmon_all_1_svc 194 In the unlikely event of more than one identical monitor 195 request, all are removed. 196*/ 197 198static int do_unmon(HostInfo *hp, my_id *idp) 199{ 200 MonList *lp, *next; 201 MonList *last = NULL; 202 int result = FALSE; 203 204 lp = hp->monList; 205 while (lp) 206 { 207 if (!strncasecmp(idp->my_name, lp->notifyHost, SM_MAXSTRLEN) 208 && (idp->my_prog == lp->notifyProg) && (idp->my_proc == lp->notifyProc) 209 && (idp->my_vers == lp->notifyVers)) 210 { 211 /* found one. Unhook from chain and free. */ 212 next = lp->next; 213 if (last) last->next = next; 214 else hp->monList = next; 215 free(lp); 216 lp = next; 217 result = TRUE; 218 } 219 else 220 { 221 last = lp; 222 lp = lp->next; 223 } 224 } 225 return (result); 226} 227 228/* sm_unmon_1 -------------------------------------------------------------- */ 229/* 230 Purpose: RPC procedure to release a monitor request. 231 Returns: Local machine's status number 232 Notes: The supplied mon_id should match the value passed in an 233 earlier call to sm_mon_1 234*/ 235 236struct sm_stat *sm_unmon_1_svc(mon_id *arg, struct svc_req *req) 237{ 238 static sm_stat res; 239 HostInfo *hp; 240 241 if (debug) 242 { 243 syslog(LOG_DEBUG, "un-monitor request for host %s", arg->mon_name); 244 syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d", 245 arg->mon_name, arg->my_id.my_name, arg->my_id.my_prog, 246 arg->my_id.my_vers, arg->my_id.my_proc); 247 } 248 249 if ((hp = find_host(arg->mon_name, FALSE))) 250 { 251 if (do_unmon(hp, &arg->my_id)) sync_file(); 252 else 253 { 254 syslog(LOG_ERR, "unmon request from %s, no matching monitor", 255 arg->my_id.my_name); 256 } 257 } 258 else syslog(LOG_ERR, "unmon request from %s for unknown host %s", 259 arg->my_id.my_name, arg->mon_name); 260 261 res.state = status_info->ourState; 262 263 return (&res); 264} 265 266/* sm_unmon_all_1 ---------------------------------------------------------- */ 267/* 268 Purpose: RPC procedure to release monitor requests. 269 Returns: Local machine's status number 270 Notes: Releases all monitor requests (if any) from the specified 271 host and program number. 272*/ 273 274struct sm_stat *sm_unmon_all_1_svc(my_id *arg, struct svc_req *req) 275{ 276 static sm_stat res; 277 HostInfo *hp; 278 int i; 279 280 if (debug) 281 { 282 syslog(LOG_DEBUG, "unmon_all for host: %s prog: %d ver: %d proc: %d", 283 arg->my_name, arg->my_prog, arg->my_vers, arg->my_proc); 284 } 285 286 for (i = status_info->noOfHosts, hp = status_info->hosts; i; i--, hp++) 287 { 288 do_unmon(hp, arg); 289 } 290 sync_file(); 291 292 res.state = status_info->ourState; 293 294 return (&res); 295} 296 297/* sm_simu_crash_1 --------------------------------------------------------- */ 298/* 299 Purpose: RPC procedure to simulate a crash 300 Returns: Nothing 301 Notes: Standardised mechanism for debug purposes 302 The specification says that we should drop all of our 303 status information (apart from the list of monitored hosts 304 on disc). However, this would confuse the rpc.lockd 305 which would be unaware that all of its monitor requests 306 had been silently junked. Hence we in fact retain all 307 current requests and simply increment the status counter 308 and inform all hosts on the monitor list. 309*/ 310 311void *sm_simu_crash_1_svc(void *v, struct svc_req *req) 312{ 313 static char dummy; 314 int work_to_do; 315 HostInfo *hp; 316 int i; 317 318 if (debug) syslog(LOG_DEBUG, "simu_crash called!!"); 319 320 /* Simulate crash by setting notify-required flag on all monitored */ 321 /* hosts, and incrementing our status number. notify_hosts() is */ 322 /* then called to fork a process to do the notifications. */ 323 324 for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++) 325 { 326 if (hp->monList) 327 { 328 work_to_do = TRUE; 329 hp->notifyReqd = TRUE; 330 } 331 } 332 status_info->ourState += 2; /* always even numbers if not crashed */ 333 334 if (work_to_do) notify_hosts(); 335 336 return (&dummy); 337} 338 339/* sm_notify_1 ------------------------------------------------------------- */ 340/* 341 Purpose: RPC procedure notifying local statd of the crash of another 342 Returns: Nothing 343 Notes: There is danger of deadlock, since it is quite likely that 344 the client procedure that we call will in turn call us 345 to remove or adjust the monitor request. 346 We therefore fork() a process to do the notifications. 347 Note that the main HostInfo structure is in a mmap() 348 region and so will be shared with the child, but the 349 monList pointed to by the HostInfo is in normal memory. 350 Hence if we read the monList before forking, we are 351 protected from the parent servicing other requests 352 that modify the list. 353*/ 354 355void *sm_notify_1_svc(stat_chge *arg, struct svc_req *req) 356{ 357 struct timeval timeout = { 20, 0 }; /* 20 secs timeout */ 358 CLIENT *cli; 359 static char dummy; 360 sm_status tx_arg; /* arg sent to callback procedure */ 361 MonList *lp; 362 HostInfo *hp; 363 pid_t pid; 364 365 if (debug) syslog(LOG_DEBUG, "notify from host %s, new state %d", 366 arg->mon_name, arg->state); 367 368 hp = find_host(arg->mon_name, FALSE); 369 if (!hp) 370 { 371 /* Never heard of this host - why is it notifying us? */ 372 syslog(LOG_ERR, "Unsolicited notification from host %s", arg->mon_name); 373 return; 374 } 375 lp = hp->monList; 376 if (!lp) return (FALSE); /* We know this host, but have no */ 377 /* outstanding requests. */ 378 pid = fork(); 379 if (pid == -1) 380 { 381 syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno)); 382 return; 383 } 384 if (pid) return (&dummy); /* Parent returns */ 385 386 while (lp) 387 { 388 tx_arg.mon_name = arg->mon_name; 389 tx_arg.state = arg->state; 390 memcpy(tx_arg.priv, lp->notifyData, sizeof(tx_arg.priv)); 391 cli = clnt_create(lp->notifyHost, lp->notifyProg, lp->notifyVers, "udp"); 392 if (!cli) 393 { 394 syslog(LOG_ERR, "Failed to contact host %s%s", lp->notifyHost, 395 clnt_spcreateerror("")); 396 } 397 else 398 { 399 if (clnt_call(cli, lp->notifyProc, xdr_sm_status, &tx_arg, xdr_void, 400 &dummy, timeout) != RPC_SUCCESS) 401 { 402 syslog(LOG_ERR, "Failed to call rpc.statd client at host %s", 403 lp->notifyHost); 404 } 405 clnt_destroy(cli); 406 } 407 lp = lp->next; 408 } 409 410 exit (0); /* Child quits */ 411} 412