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