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