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