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