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