1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* 24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 /* 31 * nislib.c 32 * 33 * This module contains the user visible functions for lookup, and list, 34 * add name service calls add/remove/modify, all information base calls 35 * add_entry/remove_entry/modify_entry, and mkdir, rmdir, and checkpoint. 36 * nis server. It should be broken up into at least three separate modules 37 * 38 */ 39 40 #include "mt.h" 41 #include <syslog.h> 42 #include <string.h> 43 #include <stdlib.h> 44 #include <ctype.h> 45 #include <rpcsvc/nis.h> 46 #include <errno.h> 47 #include "nis_clnt.h" 48 #include "nis_local.h" 49 50 /* 51 * This is *NOT* used in /usr/lib/libnsl.so.1, but has to be here so a program 52 * like "sendmail" can access it BEFORE it makes ANY getXXbyYY calls. 53 */ 54 55 mutex_t __nis_force_lookup_policy_lock = DEFAULTMUTEX; 56 uint_t __nis_force_hard_lookups = 0; 57 58 extern const nis_result __nomem_nis_result; 59 60 /* 61 * __nis_nss_lookup_policy: set's the __nis_force_hard_lookups flag 62 * to HARD_LOOKUP or SOFT_LOOKUP depending upon the argument. It will 63 * be OR-ed into the nis_list() calls in the 64 * NIS+ Name Service Switch backend /usr/lib/nss_nisplus.so.1 65 */ 66 unsigned int 67 __nis_nss_lookup_policy(unsigned int in) 68 { 69 unsigned int old; 70 71 sig_mutex_lock(&__nis_force_lookup_policy_lock); 72 old = (__nis_force_hard_lookups & HARD_LOOKUP) ? 73 HARD_LOOKUP : SOFT_LOOKUP; 74 if (in == HARD_LOOKUP) 75 __nis_force_hard_lookups = HARD_LOOKUP; 76 else if (in == SOFT_LOOKUP) 77 __nis_force_hard_lookups = 0; 78 else { 79 old = 0; 80 errno = EINVAL; 81 } 82 sig_mutex_unlock(&__nis_force_lookup_policy_lock); 83 return (old); 84 } 85 86 /* maximum number of times to loop on NIS_NOTMASTER error */ 87 #define MAX_NOTMASTER 5 88 89 /* 90 * Prototypes for static functions. 91 */ 92 static nis_result *__nis_path_list(nis_object *, int, nis_result *, 93 ib_request *, uint_t, 94 int (*)(nis_name, nis_object *, void *), 95 void *); 96 static nis_result * nis_ibops(ib_request *, rpcproc_t); 97 static nis_result * nis_nameops(nis_name, nis_object *, rpcproc_t); 98 static nis_result *nis_list_partial(nis_result *, ib_request *, uint_t, 99 int (*)(), void *); 100 101 extern int __nis_debug_rpc; 102 extern int __nis_debug_calls; 103 extern FILE *__nis_debug_file; 104 105 106 /* 107 * nis_freeresult() 108 * 109 * This function calls the XDR free function for the user to free up the 110 * memory associated with a result structure. NB: It isn't a macro because 111 * it needs to be exposed to the client. Internally the xdr routine should 112 * be used to save a procedure call. 113 */ 114 void 115 nis_freeresult(nis_result *res) 116 { 117 if (res == &__nomem_nis_result) 118 return; 119 xdr_free(xdr_nis_result, (char *)res); 120 free(res); 121 } 122 123 void 124 __nis_freelogresult(log_result *lres) 125 { 126 if (lres) { 127 xdr_free((xdrproc_t)xdr_log_result, (char *)lres); 128 free(lres); 129 } 130 } 131 132 /* 133 * nis_bind_dir() 134 * 135 * This function is used to return a binding to a candidate server. 136 * It has two side effects : 137 * 1) It keeps track of time in the cache code 138 * 2) it optionally mallocs a NIS+ result structure. 139 * 140 * 'name' contains the name of the object that will be operated on. 141 * If the operation is a lookup or a table search, then this code 142 * returns a directory object that describes that objects directory 143 * and thus a server that will have knowledge about the object. 144 * 145 * However, since the directory object, and the directory itself 146 * can be on two different machines, when listing directories we 147 * attempt to bind to the name passed. This only occurs when nis_list 148 * is called and the search criteria is NULL (which is the only legal 149 * search on a directory). 150 * 151 */ 152 nis_error 153 nis_bind_dir( 154 char *dname, 155 int parent_first, 156 nis_bound_directory **binding, 157 uint_t flags) 158 { 159 nis_error stat; 160 161 if (parent_first) { 162 stat = __nis_CacheBindDir(nis_domain_of(dname), 163 binding, flags); 164 if (stat != NIS_SUCCESS) 165 stat = __nis_CacheBindDir(dname, binding, flags); 166 } else { 167 stat = __nis_CacheBindDir(dname, binding, flags); 168 if (stat != NIS_SUCCESS) 169 stat = __nis_CacheBindDir(nis_domain_of(dname), 170 binding, flags); 171 } 172 return (stat); 173 } 174 175 nis_error 176 nis_bind_server(nis_server *srv, int nsrv, nis_bound_directory **binding) 177 { 178 nis_error stat; 179 180 stat = __nis_CacheBindServer(srv, nsrv, binding); 181 return (stat); 182 } 183 184 /* 185 * __nis_path_list() 186 * 187 * This internal function will list all of the tables in a path. 188 * if the ALL_RESULTS flag is set, it keeps on going, and going. 189 * otherwise it returns on the first match. 190 * 191 * NB: The nis_list() function initializes the request's search 192 * criteria, we just swap in table names. 193 * 194 * All possible returns from call to NIS_IBLIST (all NIS_xxx) 195 * 196 * Successful returns 197 * 198 * CBRESULTS, SUCCESS, S_SUCCESS - found something 199 * NOTFOUND, PARTIAL, - looked but no data. 200 * PERMISSION - can't read the table 201 * 202 * Errors that generate syslog warnings 203 * NOTMASTER, NOT_ME, BADNAME, NOSUCHTABLE, NOSUCHNAME 204 * BADATTRIBUTE, INVALIDOBJ 205 * 206 * Errors that fail silently but change final result to "soft" 207 * NOMEMORY, TRYAGAIN, SYSTEMERROR, BADOBJECT 208 * 209 * Fatal errors : 210 * NOCALLBACK 211 */ 212 static nis_result * 213 __nis_path_list( 214 nis_object *tbl_obj, /* Path of tables to search */ 215 int sf, /* search first object */ 216 nis_result *res, /* result structure to use */ 217 ib_request *req, /* Request structure */ 218 unsigned int flags, /* Flags to this function */ 219 int (*cback)(nis_name, nis_object *, void *), 220 void *cbdata) 221 { 222 nis_name pathlist[NIS_MAXPATHDEPTH]; /* Parsed table path */ 223 char pathbuf[NIS_MAXPATHLEN]; 224 char firstpath[NIS_MAXNAMELEN]; 225 nis_result *local_res; /* local result */ 226 nis_name table; /* current table */ 227 int tnum, /* # of tables to search */ 228 i, j, /* counters */ 229 cur_obj, /* obj, link counters */ 230 soft_error = 0; /* error detected */ 231 unsigned int aticks = 0, /* profiling vars */ 232 dticks = 0, 233 cticks = 0, 234 zticks = 0; 235 nis_object *obj_list; /* list returned from call */ 236 int num_objs, 237 total_objs; /* # of objects returned */ 238 struct obj_lists { /* returned objects from each */ 239 nis_object *objs; 240 int len; 241 } ret_objs[NIS_MAXPATHDEPTH]; 242 243 /* construct a list of tables to search */ 244 (void) strncpy(pathbuf, tbl_obj->TA_data.ta_path, NIS_MAXPATHLEN); 245 if (sf) { 246 (void) snprintf(firstpath, sizeof (firstpath), 247 "%s.%s", tbl_obj->zo_name, 248 tbl_obj->zo_domain); 249 pathlist[0] = firstpath; 250 tnum = __nis_parse_path(pathbuf, &pathlist[1], 251 NIS_MAXPATHDEPTH - 1); 252 tnum++; 253 } else 254 tnum = __nis_parse_path(pathbuf, pathlist, NIS_MAXPATHDEPTH); 255 256 /* Take any existing objects from the result passed in */ 257 ret_objs[0].objs = res->objects.objects_val; 258 ret_objs[0].len = res->objects.objects_len; 259 total_objs = ret_objs[0].len; 260 res->objects.objects_val = NULL; 261 res->objects.objects_len = 0; 262 263 /* 264 * Either search until a match is found, or if ALL_RESULTS is 265 * set, search until path is exhausted. 266 */ 267 for (i = 0; i < tnum; i++) { 268 table = pathlist[i]; 269 /* Ignore non-fully qualified names in path */ 270 if (table[strlen(table) - 1] != '.') { 271 syslog(LOG_WARNING, 272 "nis_list: non fully qualified name in table path, %s, ignored.\n", 273 table); 274 continue; 275 } 276 /* swap in the table name from the path */ 277 req->ibr_name = table; 278 /* prepare to receive the objects returned */ 279 ret_objs[i+1].objs = NULL; 280 ret_objs[i+1].len = 0; 281 if (cback) { 282 (void) mutex_lock(&__nis_callback_lock); 283 local_res = __nis_core_lookup(req, flags, 1, cbdata, 284 cback); 285 (void) mutex_unlock(&__nis_callback_lock); 286 } else 287 local_res = __nis_core_lookup(req, flags, 1, cbdata, 288 cback); 289 aticks += local_res->aticks; 290 dticks += local_res->dticks; 291 cticks += local_res->cticks; 292 zticks += local_res->zticks; 293 obj_list = local_res->objects.objects_val; 294 num_objs = local_res->objects.objects_len; 295 296 switch (local_res->status) { 297 case NIS_SUCCESS : 298 /* put these into the array */ 299 ret_objs[i+1].objs = obj_list; 300 ret_objs[i+1].len = num_objs; 301 total_objs += num_objs; 302 /* zero this so freeresult won't free them */ 303 local_res->objects.objects_val = NULL; 304 local_res->objects.objects_len = 0; 305 /* fall through to the CBRESULTS code */ 306 /*FALLTHROUGH*/ 307 case NIS_CBRESULTS : 308 case NIS_CBERROR : 309 break; 310 case NIS_PARTIAL : 311 case NIS_PERMISSION : 312 case NIS_NOTMASTER : 313 /* these errors, just break */ 314 break; 315 case NIS_LINKNAMEERROR : /* message generated above */ 316 soft_error = TRUE; 317 break; 318 case NIS_NOT_ME : 319 case NIS_RPCERROR : 320 case NIS_NAMEUNREACHABLE : 321 /* generate message and set soft_error */ 322 syslog(LOG_WARNING, 323 "nis_list: NIS+ error %s encountered on name %s in table %s.%s's path.", 324 nis_sperrno(local_res->status), 325 table, tbl_obj->zo_name, tbl_obj->zo_domain); 326 soft_error = TRUE; 327 break; 328 case NIS_NOTFOUND : 329 case NIS_BADNAME : 330 case NIS_NOSUCHTABLE : 331 case NIS_NOSUCHNAME : 332 case NIS_BADATTRIBUTE : 333 case NIS_INVALIDOBJ : 334 /* generate message but don't set soft_error */ 335 syslog(LOG_WARNING, 336 "nis_list: NIS+ error %s encountered on name %s in table %s.%s's path.", 337 nis_sperrno(local_res->status), 338 table, tbl_obj->zo_name, tbl_obj->zo_domain); 339 break; 340 default : 341 soft_error = TRUE; 342 break; 343 } 344 /* 345 * POLICY : When one table in a path is unreachable, 346 * should we continue on or stop with an error? 347 * ANSWER : Continue on. Loss of a portion of the namespace 348 * should not cause disruptions in all of the namespace. 349 * NB: This can have interesting side effects such that a 350 * name may suddenly change "value" because it is being 351 * resolved from a different place. 352 * 353 * If we're not returning all results and we've had a 354 * successful call, we just return those results. 355 */ 356 if (((flags & ALL_RESULTS) == 0) && 357 ((local_res->status == NIS_SUCCESS) || 358 (local_res->status == NIS_CBRESULTS))) { 359 res->status = local_res->status; 360 res->aticks += aticks; 361 res->dticks += dticks; 362 res->zticks += zticks; 363 res->objects.objects_val = obj_list; 364 res->objects.objects_len = num_objs; 365 /* reset so that caller does not free it */ 366 req->ibr_name = NULL; 367 nis_freeresult(local_res); 368 /* return same result structure back to them */ 369 return (res); 370 } 371 372 /* otherwise just free local result (we've got the objs) */ 373 nis_freeresult(local_res); 374 } 375 376 /* name is already freed so null this out */ 377 req->ibr_name = NULL; 378 379 /* 380 * At this point, we've either exhausted the list of tables 381 * (total_objs == 0), or we've asked for all results so the 382 * ret_objs[] array has some data in it (total_objs > 0) 383 * if soft_error is set we will adjust our result status 384 * appropriately. 385 */ 386 if (total_objs) { 387 /* now build a list of objects that should be returned */ 388 obj_list = calloc(total_objs, sizeof (nis_object)); 389 if (obj_list == NULL) { 390 res->status = NIS_NOMEMORY; 391 res->aticks += aticks; 392 res->dticks += dticks; 393 res->zticks += zticks; 394 for (i = 0; i < (tnum+1); i++) { 395 if (ret_objs[i].objs == NULL) 396 continue; 397 for (j = 0; j < ret_objs[i].len; j++) 398 xdr_free(xdr_nis_object, 399 (char *)&(ret_objs[i].objs[j])); 400 free(ret_objs[i].objs); 401 } 402 return (res); 403 } 404 405 /* copyout all objects into this new array */ 406 cur_obj = 0; 407 for (i = 0; i < (tnum+1); i++) { 408 if (ret_objs[i].objs == NULL) 409 continue; 410 for (j = 0; j < ret_objs[i].len; j++) 411 obj_list[cur_obj++] = ret_objs[i].objs[j]; 412 free(ret_objs[i].objs); 413 } 414 res->objects.objects_val = obj_list; 415 res->objects.objects_len = cur_obj; 416 if (cur_obj) 417 res->status = NIS_SUCCESS; 418 else 419 res->status = NIS_NOTFOUND; 420 } else { 421 if (cback) 422 res->status = NIS_CBRESULTS; 423 else 424 res->status = NIS_NOTFOUND; 425 } 426 427 if (soft_error && (res->status == NIS_SUCCESS)) 428 res->status = NIS_S_SUCCESS; 429 else if (soft_error && (res->status == NIS_NOTFOUND)) 430 res->status = NIS_S_NOTFOUND; 431 res->aticks += aticks; 432 res->dticks += dticks; 433 res->cticks += cticks; 434 res->zticks += zticks; 435 return (res); 436 } 437 438 /* 439 * nis_lookup() 440 * 441 * This is the main lookup function of the name service. It will look 442 * for the named object and return it. If the object was a link and 443 * the flag FOLLOW_LINKS was set it will look up the item named by 444 * the LINK, if that is an indexed name the lookup may return multiple 445 * objects. If the name is not fully qualified and EXPAND_NAME is set 446 * this function will expand the name into several candidate names. 447 */ 448 nis_result * 449 nis_lookup(nis_name name, uint_t flags) 450 { 451 nis_error nis_err = NIS_SUCCESS; 452 nis_name *namelist; 453 nis_result *res; 454 ib_request req; 455 int i; 456 unsigned int aticks = 0, 457 cticks = 0, 458 dticks = 0, 459 zticks = 0; 460 461 (void) __start_clock(CLOCK_CLIENT); 462 __nis_CacheStart(); 463 if (__nis_debug_calls) { 464 (void) fprintf(__nis_debug_file, "nis_lookup(%s, 0x%x)\n", 465 name, flags); 466 } 467 (void) memset((char *)&req, 0, sizeof (ib_request)); 468 req.ibr_name = name; 469 i = (int)strlen(name); 470 if ((flags & EXPAND_NAME) == 0 || (i > 0 && name[i-1] == '.')) { 471 res = __nis_core_lookup(&req, flags, 0, NULL, NULL); 472 res->cticks = __stop_clock(CLOCK_CLIENT); 473 if (__nis_debug_calls) 474 __nis_print_result(res); 475 return (res); 476 } 477 namelist = __nis_getnames(name, &nis_err); 478 if (! namelist) { 479 res = nis_make_error(nis_err, 0, 0, 0, 0); 480 res->cticks = __stop_clock(CLOCK_CLIENT); 481 if (__nis_debug_calls) 482 __nis_print_result(res); 483 return (res); 484 } 485 for (i = 0; namelist[i]; i++) { 486 req.ibr_name = namelist[i]; 487 res = __nis_core_lookup(&req, flags, 0, NULL, NULL); 488 switch (res->status) { 489 /* 490 * All of the errors that indicate the name 491 * is bound. 492 * NB: We include the "nis_list" errors as well 493 * as the core_lookup call could have followed 494 * a link into a table operation. 495 */ 496 case NIS_SUCCESS : 497 case NIS_PARTIAL : 498 case NIS_CBRESULTS : 499 case NIS_CBERROR : 500 case NIS_CLNTAUTH : 501 case NIS_SRVAUTH : 502 case NIS_PERMISSION : 503 case NIS_LINKNAMEERROR: 504 case NIS_NOTMASTER : 505 res->aticks += aticks; 506 res->dticks += dticks; 507 res->zticks += zticks; 508 res->cticks += cticks; 509 res->cticks += __stop_clock(CLOCK_CLIENT); 510 nis_freenames(namelist); 511 if (__nis_debug_calls) 512 __nis_print_result(res); 513 return (res); 514 default : 515 aticks += res->aticks; 516 cticks += res->cticks; 517 dticks += res->dticks; 518 zticks += res->zticks; 519 if (nis_err == NIS_SUCCESS) 520 nis_err = res->status; 521 nis_freeresult(res); 522 } 523 } 524 nis_freenames(namelist); 525 cticks += __stop_clock(CLOCK_CLIENT); 526 if (nis_err == NIS_SUCCESS) { 527 syslog(LOG_WARNING, "nis_lookup: empty namelist"); 528 nis_err = NIS_NOTFOUND; /* fix up in case namelist empty */ 529 } 530 res = nis_make_error(nis_err, aticks, cticks, dticks, zticks); 531 if (__nis_debug_calls) 532 __nis_print_result(res); 533 return (res); 534 } 535 536 /* 537 * nis_list() 538 * 539 * This function takes a "standard" NIS name with embedded search criteria 540 * and does a list on the object. 541 */ 542 nis_result * 543 nis_list( 544 nis_name name, /* list name like '[foo=bar].table.name' */ 545 uint_t flags, /* Flags for the search */ 546 int (*cback)(), /* Callback function. */ 547 void *cbdata) /* Callback private data */ 548 { 549 nis_error nis_err = NIS_SUCCESS; 550 nis_name *namelist; 551 nis_object *obj; 552 nis_result *res; 553 ib_request req; 554 nis_error stat; 555 uint32_t zticks = 0, 556 aticks = 0, 557 dticks = 0, 558 cticks = 0; 559 int i, done; 560 561 /* start the client profiling clock */ 562 (void) __start_clock(CLOCK_CLIENT); 563 564 __nis_CacheStart(); 565 if (__nis_debug_calls) { 566 (void) fprintf(__nis_debug_file, 567 "nis_list(%s, 0x%x, 0x%p, 0x%p)\n", name, flags, 568 (void *)cback, (void *)cbdata); 569 } 570 571 /* Parse the request into a table name and attr/value pairs */ 572 stat = nis_get_request(name, NULL, NULL, &req); 573 if (stat != NIS_SUCCESS) { 574 res = nis_make_error(stat, 0, 0, 0, 0); 575 res->cticks = __stop_clock(CLOCK_CLIENT); 576 if (__nis_debug_calls) 577 __nis_print_result(res); 578 return (res); 579 } 580 581 /* 582 * process the ALL_RESULTS flag specially. First fetch 583 * the table object to get the path, then call path list 584 * to read all of the data. Note if we returned the object 585 * on a list we could save an RPC here. 586 */ 587 if (flags & ALL_RESULTS) { 588 res = nis_lookup(req.ibr_name, flags); 589 if (res->status != NIS_SUCCESS) { 590 nis_free_request(&req); 591 if (__nis_debug_calls) 592 __nis_print_result(res); 593 return (res); 594 } 595 aticks = res->aticks; 596 cticks = res->cticks; 597 dticks = res->dticks; 598 zticks = res->zticks; 599 obj = res->objects.objects_val; 600 if ((res->objects.objects_len > 1) || 601 (__type_of(obj) != NIS_TABLE_OBJ)) { 602 /* Note : can't do all results on directory obj. */ 603 xdr_free(xdr_nis_result, (char *)res); 604 nis_free_request(&req); 605 (void) memset((char *)res, 0, sizeof (nis_result)); 606 res->status = NIS_BADOBJECT; 607 res->aticks = aticks; 608 res->dticks = dticks; 609 res->cticks = cticks; 610 res->zticks = zticks; 611 if (__nis_debug_calls) 612 __nis_print_result(res); 613 return (res); 614 } 615 res->objects.objects_val = NULL; 616 res->objects.objects_len = 0; 617 free(req.ibr_name); /* won't be needing this */ 618 req.ibr_name = NULL; 619 res = __nis_path_list(obj, 1, res, &req, flags, cback, cbdata); 620 nis_free_request(&req); 621 xdr_free(xdr_nis_object, (char *)obj); 622 free(obj); 623 res->aticks += aticks; 624 res->dticks += dticks; 625 res->cticks += cticks; 626 res->zticks += zticks; 627 if (__nis_debug_calls) 628 __nis_print_result(res); 629 return (res); 630 } 631 632 /* 633 * Normal requests. The server will return NIS_PARTIAL 634 * if we specify a search criteria and the table exists 635 * but the entry does not exist within the table. We 636 * need to handle this return value by checking for a 637 * table path in nis_list_partial(). 638 */ 639 i = (int)strlen(name); 640 if ((flags & EXPAND_NAME) == 0 || (i > 0 && name[i-1] == '.')) { 641 if (cback) { 642 (void) mutex_lock(&__nis_callback_lock); 643 res = __nis_core_lookup(&req, flags, 1, cbdata, cback); 644 (void) mutex_unlock(&__nis_callback_lock); 645 } else 646 res = __nis_core_lookup(&req, flags, 1, cbdata, cback); 647 free(req.ibr_name); 648 if (res->status == NIS_PARTIAL) 649 res = nis_list_partial(res, &req, flags, cback, cbdata); 650 } else { 651 namelist = __nis_getnames(req.ibr_name, &stat); 652 if (! namelist) { 653 res = nis_make_error(stat, 0, 0, 0, 0); 654 nis_free_request(&req); 655 res->cticks = __stop_clock(CLOCK_CLIENT); 656 if (__nis_debug_calls) 657 __nis_print_result(res); 658 return (res); 659 } 660 free(req.ibr_name); /* non fully qualified name */ 661 662 for (i = 0, done = 0; !done && namelist[i]; i++) { 663 /* replace with the candidate name */ 664 req.ibr_name = namelist[i]; 665 if (cback) { 666 (void) mutex_lock(&__nis_callback_lock); 667 res = __nis_core_lookup(&req, flags, 1, cbdata, 668 cback); 669 (void) mutex_unlock(&__nis_callback_lock); 670 } else 671 res = __nis_core_lookup(&req, flags, 1, cbdata, 672 cback); 673 if (res->status == NIS_PARTIAL) 674 res = nis_list_partial(res, &req, 675 flags, cback, cbdata); 676 switch (res->status) { 677 /* 678 * All of the errors that indicate the name 679 * is bound. 680 */ 681 case NIS_SUCCESS : 682 case NIS_CBRESULTS : 683 case NIS_CBERROR : 684 case NIS_CLNTAUTH : 685 case NIS_SRVAUTH : 686 case NIS_PERMISSION : 687 case NIS_NOTMASTER : 688 done = 1; 689 break; 690 default : 691 aticks += res->aticks; 692 cticks += res->cticks; 693 dticks += res->dticks; 694 zticks += res->zticks; 695 if (nis_err == NIS_SUCCESS) 696 nis_err = res->status; 697 nis_freeresult(res); 698 break; 699 } 700 } 701 if (! done) { 702 if (nis_err == NIS_SUCCESS) { 703 syslog(LOG_WARNING, "nis_list: empty namelist"); 704 nis_err = NIS_NOTFOUND; /* if empty namelist */ 705 } 706 res = nis_make_error(nis_err, aticks, cticks, 707 dticks, zticks); 708 } 709 nis_freenames(namelist); /* not needed any longer */ 710 } 711 req.ibr_name = NULL; /* already freed */ 712 713 /* 714 * Returns from __nis_core_lookup : 715 * NIS_SUCCESS, Table/Name found, search suceeded. 716 * NIS_CBRESULTS, Table/Name found, search suceeded to callback 717 * NIS_PARTIAL, found the name but didn't match any entries. 718 * NIS_CLNTAUTH, Found table, couldn't authenticate callback 719 * NIS_SRVAUTH Found table, couldn't authenticate server 720 * NIS_PERMISSION Found table, couldn't read it. 721 * NIS_NOTMASTER Found table, wasn't master (master requested) 722 * NIS_RPCERROR unable to communicate with service. 723 * NIS_XXX Error somewhere. 724 * 725 */ 726 res->aticks += aticks; 727 res->cticks += cticks; 728 res->dticks += dticks; 729 res->zticks += zticks; 730 res->cticks += __stop_clock(CLOCK_CLIENT); 731 nis_free_request(&req); 732 if (__nis_debug_calls) 733 __nis_print_result(res); 734 return (res); 735 } 736 737 /* 738 * Deal with a "PARTIAL" result. Given a NIS name of 739 * [search-criteria].table-name, this occurs when the 740 * server found an object whose name was 'table-name' but 741 * either the object couldn't be searched because it was the 742 * wrong type or the search resulted in no results. 743 * 744 * If the object that matched 'table-name' was a LINK object 745 * core lookup will have followed it for us. 746 * 747 * We increment a local copy of the statistics and then reset 748 * them in 'res' before returning. 749 */ 750 static 751 nis_result * 752 nis_list_partial( 753 nis_result *res, 754 ib_request *req, 755 uint_t flags, /* Flags for the search */ 756 int (*cback)(), /* Callback function. */ 757 void *cbdata) /* Callback private data */ 758 { 759 nis_object *obj; 760 table_obj *tdata; 761 unsigned int aticks = res->aticks; 762 unsigned int cticks = res->cticks; 763 unsigned int dticks = res->dticks; 764 unsigned int zticks = res->zticks; 765 766 obj = res->objects.objects_val; 767 if (__type_of(obj) == NIS_DIRECTORY_OBJ) { 768 /* 769 * POLICY : What is the error when you search a 770 * a DIRECTORY and the results are no entries ? 771 * ANSWER : A NOT FOUND error, assuming the server 772 * did not return a "bad attribute" error, AND 773 * we do _NOT_ return the directory object that the 774 * server returned. 775 */ 776 xdr_free(xdr_nis_result, (char *)res); 777 (void) memset((char *)res, 0, sizeof (nis_result)); 778 res->status = NIS_NOTFOUND; 779 } else if (__type_of(obj) == NIS_LINK_OBJ) { 780 /* If the object that matched 'table-name' was a LINK object */ 781 /* core lookup will have followed it for us. If it's not */ 782 /* found there and we somehow came to nis_list_partial() */ 783 /* we need to return NIS_NOTFOUND */ 784 785 xdr_free(xdr_nis_result, (char *)res); 786 (void) memset((char *)res, 0, sizeof (nis_result)); 787 res->status = NIS_NOTFOUND; 788 } else if (__type_of(obj) != NIS_TABLE_OBJ) { 789 /* 790 * This shouldn't happen because the server should 791 * catch it when it attempts the search. 792 */ 793 xdr_free(xdr_nis_result, (char *)res); 794 (void) memset((char *)res, 0, sizeof (nis_result)); 795 res->status = NIS_NOTSEARCHABLE; 796 } else { 797 /* 798 * Now we know its a table object and that our search failed. 799 */ 800 tdata = &(obj->TA_data); 801 if (((flags & FOLLOW_PATH) != 0) && (tdata->ta_path) && 802 (strlen(tdata->ta_path) > (size_t)0)) { 803 obj = res->objects.objects_val; 804 res->objects.objects_val = NULL; 805 res->objects.objects_len = 0; 806 res = __nis_path_list(obj, 0, res, req, flags, 807 cback, cbdata); 808 /* free up the table object */ 809 xdr_free(xdr_nis_object, (char *)obj); 810 free(obj); 811 /* ticks are updated by __nis_path_list */ 812 aticks = res->aticks; 813 cticks = res->cticks; 814 dticks = res->dticks; 815 zticks = res->zticks; 816 } else { 817 xdr_free(xdr_nis_result, (char *)res); 818 (void) memset((char *)res, 0, sizeof (nis_result)); 819 /* 820 * If a search criteria was specified, indicate 821 * that we didn't find the entry. If there was 822 * no search criteria, then we return success 823 * (i.e., table was listed successfully, but 824 * there were no entries in the table). 825 */ 826 if (req->ibr_srch.ibr_srch_len) 827 res->status = NIS_NOTFOUND; 828 else if (cback) 829 res->status = NIS_CBRESULTS; 830 else 831 res->status = NIS_SUCCESS; 832 } 833 } 834 res->aticks = aticks; 835 res->dticks = dticks; 836 res->zticks = zticks; 837 res->cticks = cticks; 838 return (res); 839 } 840 841 /* 842 * Make a call to a nis+ server based on the call state in 'state'. 843 * We loop until we either get a response or until we can no longer 844 * get a client handle to any server. 845 */ 846 nis_error 847 nis_call(nis_call_state *state, rpcproc_t func, 848 xdrproc_t req_proc, char *req, xdrproc_t res_proc, char *res) 849 { 850 CLIENT *clnt; 851 nis_error err = NIS_SUCCESS; 852 enum clnt_stat status; 853 854 for (;;) { 855 clnt = __nis_get_server(state); 856 if (clnt == NULL) { 857 err = state->niserror; 858 break; 859 } 860 861 if (__nis_debug_rpc) 862 __nis_print_call(clnt, func); 863 864 status = clnt_call(clnt, func, 865 req_proc, req, res_proc, res, 866 state->timeout); 867 868 if (__nis_debug_rpc) 869 __nis_print_rpc_result(status); 870 871 __nis_release_server(state, clnt, status); 872 873 if (status == RPC_SUCCESS) 874 break; 875 } 876 877 return (err); 878 } 879 880 /* 881 * nis_nameops() 882 * 883 * This generic function calls all of the name operations. 884 */ 885 886 static nis_result * 887 nis_nameops(nis_name name, nis_object *obj, rpcproc_t func) 888 { 889 nis_call_state state; 890 nis_result *res; 891 nis_error err; 892 ns_request req; 893 nis_name oname, odomain; 894 nis_name oowner, ogroup; 895 char nname[1024], ndomain[1024]; 896 nis_name tname; 897 int times = 0; 898 899 if (name != 0 && strlen(name) >= NIS_MAXNAMELEN) { 900 return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0)); 901 } 902 903 if (obj) { 904 /* 905 * Enforce correct name policy on NIS+ objects stored 906 * into the namespace. This code insures that zo_name 907 * and zo_domain are correct. 908 */ 909 oname = obj->zo_name; 910 if ((tname = nis_leaf_of(name)) == NULL || 911 strlcpy(nname, tname, sizeof (nname)) >= sizeof (nname)) 912 return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0)); 913 obj->zo_name = nname; 914 odomain = obj->zo_domain; 915 if ((tname = nis_domain_of(name)) == NULL || 916 strlcpy(ndomain, tname, sizeof (ndomain)) 917 >= sizeof (ndomain)) 918 return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0)); 919 obj->zo_domain = ndomain; 920 if (ndomain[strlen(ndomain)-1] != '.' && 921 strlcat(ndomain, ".", sizeof (ndomain)) >= 922 sizeof (ndomain)) 923 return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0)); 924 925 oowner = obj->zo_owner; 926 if (obj->zo_owner == 0) 927 obj->zo_owner = nis_local_principal(); 928 929 ogroup = obj->zo_group; 930 if (obj->zo_group == 0) 931 obj->zo_group = nis_local_group(); 932 } 933 934 (void) memset((char *)&req, 0, sizeof (req)); 935 req.ns_name = name; 936 if (obj) { 937 req.ns_object.ns_object_len = 1; 938 req.ns_object.ns_object_val = obj; 939 } else { 940 req.ns_object.ns_object_len = 0; 941 req.ns_object.ns_object_val = NULL; 942 } 943 944 __nis_init_call_state(&state); 945 state.name = name; 946 state.flags = MASTER_ONLY; 947 state.parent_first = 1; 948 949 res = calloc(1, sizeof (nis_result)); 950 if (res == NULL) 951 return (nis_make_error(NIS_NOMEMORY, 0, 0, 0, 0)); 952 953 again: 954 err = nis_call(&state, func, 955 (xdrproc_t)xdr_ns_request, (char *)&req, 956 (xdrproc_t)xdr_nis_result, (char *)res); 957 if (err == NIS_SUCCESS && res->status == NIS_NOTMASTER && 958 times++ < MAX_NOTMASTER) 959 goto again; 960 res->aticks = state.aticks; 961 __nis_reset_call_state(&state); 962 if (err != NIS_SUCCESS) 963 res->status = err; 964 965 if (obj) { 966 obj->zo_name = oname; 967 obj->zo_domain = odomain; 968 obj->zo_owner = oowner; 969 obj->zo_group = ogroup; 970 } 971 return (res); 972 } 973 974 /* 975 * nis_add() 976 * 977 * This function will add an object to the namespace. If it is a 978 * table type object the server will create a table for it as well. 979 */ 980 981 nis_result * 982 nis_add(nis_name name, nis_object *obj) 983 { 984 nis_result *res; 985 986 (void) __start_clock(CLOCK_CLIENT); /* start the client clock */ 987 __nis_CacheStart(); 988 if (__nis_debug_calls) { 989 (void) fprintf(__nis_debug_file, "nis_add(%s, 0x%p\n", 990 name?name:"(nil)", (void *)obj); 991 } 992 res = nis_nameops(name, obj, NIS_ADD); 993 res->cticks = __stop_clock(CLOCK_CLIENT); 994 if (__nis_debug_calls) 995 __nis_print_result(res); 996 return (res); 997 } 998 999 static void 1000 nis_flush_cache(nis_name name, nis_object *obj) 1001 { 1002 if (obj == 0 || (obj && __type_of(obj) == NIS_DIRECTORY_OBJ)) { 1003 directory_obj dobj; 1004 if (__nis_CacheSearch(name, &dobj) == NIS_SUCCESS && 1005 nis_dir_cmp(name, dobj.do_name) == SAME_NAME) { 1006 __nis_CacheRemoveEntry(&dobj); 1007 xdr_free((xdrproc_t)xdr_directory_obj, (char *)&dobj); 1008 } 1009 } 1010 } 1011 1012 /* 1013 * nis_remove() 1014 * 1015 * This function will remove an object from the namespace. If it is a 1016 * table type object the server will destroy the table for it as well. 1017 */ 1018 1019 nis_result * 1020 nis_remove(nis_name name, nis_object *obj) 1021 { 1022 nis_result *res; 1023 1024 (void) __start_clock(CLOCK_CLIENT); /* start the client clock */ 1025 __nis_CacheStart(); 1026 if (__nis_debug_calls) { 1027 (void) fprintf(__nis_debug_file, "nis_remove(%s, 0x%p)\n", 1028 name?name:"(nil)", (void *)obj); 1029 } 1030 res = nis_nameops(name, obj, NIS_REMOVE); 1031 if (res->status == NIS_SUCCESS) 1032 nis_flush_cache(name, obj); 1033 res->cticks = __stop_clock(CLOCK_CLIENT); 1034 if (__nis_debug_calls) 1035 __nis_print_result(res); 1036 return (res); 1037 } 1038 1039 /* 1040 * nis_modify() 1041 * 1042 * This function will modify an object in the namespace. 1043 */ 1044 1045 nis_result * 1046 nis_modify(nis_name name, nis_object *obj) 1047 { 1048 nis_result *res; 1049 1050 (void) __start_clock(CLOCK_CLIENT); /* start the client clock */ 1051 __nis_CacheStart(); 1052 if (__nis_debug_calls) { 1053 (void) fprintf(__nis_debug_file, "nis_modify(%s, 0x%p)\n", 1054 name?name:"(nil)", (void *)obj); 1055 } 1056 res = nis_nameops(name, obj, NIS_MODIFY); 1057 if (res->status == NIS_SUCCESS) 1058 nis_flush_cache(name, obj); 1059 res->cticks = __stop_clock(CLOCK_CLIENT); 1060 if (__nis_debug_calls) 1061 __nis_print_result(res); 1062 return (res); 1063 } 1064 1065 1066 /* 1067 * The cookie has the name of the server (null-terminated), followed 1068 * by the "real" cookie from the NIS server. We make a copy of the 1069 * server name, copy the "real" cookie to the beginning of the buffer, 1070 * and then adjust the cookie length. A zero-length cookie means 1071 * that the cookie is not valid and we return 0. 1072 */ 1073 static 1074 char * 1075 cookie_to_name(netobj *cookie) 1076 { 1077 size_t len; 1078 size_t offset; 1079 char *s; 1080 1081 if (cookie->n_bytes == 0) 1082 return (0); 1083 if ((s = strdup(cookie->n_bytes)) == 0) { 1084 syslog(LOG_ERR, "cookie_to_name: strdup failed"); 1085 return (0); 1086 } 1087 offset = strlen(s) + 1; 1088 len = cookie->n_len - offset; 1089 (void) memmove(cookie->n_bytes, cookie->n_bytes + offset, len); 1090 cookie->n_len = (uint_t)len; 1091 1092 return (s); 1093 } 1094 1095 /* 1096 * We store a server name in a cookie along with the "real" 1097 * cookie from the NIS server. The new cookie will include 1098 * the server name (null-terminated) with the "real" cookie 1099 * following it. If we can't allocate memory, we set the 1100 * cookie length to 0 to indicate that it is an invalid cookie. 1101 */ 1102 static 1103 void 1104 name_to_cookie(char *name, nis_result *res) 1105 { 1106 size_t len; 1107 size_t offset; 1108 netobj *cookie = &res->cookie; 1109 char *p; 1110 1111 offset = strlen(name) + 1; 1112 len = offset + res->cookie.n_len; 1113 p = malloc(len); 1114 if (p == 0) { 1115 cookie->n_len = 0; /* indicates a bad cookie */ 1116 syslog(LOG_ERR, "name_to_cookie: malloc failed"); 1117 return; 1118 } 1119 (void) strcpy(p, name); 1120 (void) memmove(p+offset, res->cookie.n_bytes, res->cookie.n_len); 1121 free(res->cookie.n_bytes); 1122 res->cookie.n_bytes = p; 1123 res->cookie.n_len = (uint_t)len; 1124 } 1125 1126 /* 1127 * nis_ibops() 1128 * 1129 * This generic function calls all of the table operations. 1130 * 1131 * Note that although we use a virtual circuit, there are no keepalives. 1132 * Because of this, the length of the timeout is vital, and we attempt 1133 * to tune it here for the various operations. 1134 */ 1135 1136 /* a single modify has been seen to take as long as 180 sec. */ 1137 #define NIS_MODIFY_TIMEOUT 300 /* 5 minutes */ 1138 1139 /* netgroup.org_dir.ssi has 48K entries, and will take almost this long */ 1140 #define NIS_REMOVE_MULT_TIMEOUT (2*60*60) /* 2 hours */ 1141 1142 static nis_result * 1143 nis_ibops(ib_request *req, rpcproc_t func) 1144 { 1145 nis_result *res; 1146 nis_object *obj = NULL; 1147 nis_name oname, odomain; 1148 nis_name oowner, ogroup; 1149 nis_name tname; 1150 char nname[NIS_MAXNAMELEN], ndomain[NIS_MAXNAMELEN]; 1151 int timeout = NIS_GEN_TIMEOUT; /* in seconds */ 1152 nis_error call_err; 1153 nis_call_state state; 1154 int make_cookie = FALSE; 1155 char *server_name = NULL; 1156 uint_t flags = 0; 1157 int times = 0; 1158 extern char *__nis_server_name(nis_call_state *); 1159 1160 if (req->ibr_obj.ibr_obj_len) { 1161 /* 1162 * Enforce correct name policy on objects stored into 1163 * tables. This code insures that zo_name and zo_domain 1164 * are correct. 1165 */ 1166 obj = req->ibr_obj.ibr_obj_val; 1167 oname = obj->zo_name; 1168 tname = nis_leaf_of(req->ibr_name); 1169 if (tname == NULL || strlcpy(nname, tname, sizeof (nname)) >= 1170 sizeof (nname)) 1171 return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0)); 1172 obj->zo_name = nname; 1173 odomain = obj->zo_domain; 1174 tname = nis_domain_of(req->ibr_name); 1175 if (tname == NULL || strlcpy(ndomain, tname, 1176 sizeof (ndomain)) >= sizeof (ndomain)) 1177 return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0)); 1178 obj->zo_domain = ndomain; 1179 if (ndomain[strlen(ndomain)-1] != '.' && 1180 strlcat(ndomain, ".", sizeof (ndomain)) >= 1181 sizeof (ndomain)) 1182 return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0)); 1183 1184 oowner = obj->zo_owner; 1185 if (obj->zo_owner == 0) 1186 obj->zo_owner = nis_local_principal(); 1187 1188 ogroup = obj->zo_group; 1189 if (obj->zo_group == 0) 1190 obj->zo_owner = nis_local_group(); 1191 } 1192 1193 res = calloc(1, sizeof (nis_result)); 1194 if (res == NULL) 1195 return (nis_make_error(NIS_NOMEMORY, 0, 0, 0, 0)); 1196 1197 /* determine the timeout (heuristic) */ 1198 switch (func) { 1199 case NIS_IBMODIFY: 1200 flags = MASTER_ONLY; 1201 timeout = NIS_MODIFY_TIMEOUT; 1202 break; 1203 case NIS_IBREMOVE: 1204 flags = MASTER_ONLY; 1205 if (req->ibr_flags & REM_MULTIPLE) 1206 timeout = NIS_REMOVE_MULT_TIMEOUT; 1207 else 1208 timeout = NIS_MODIFY_TIMEOUT; 1209 break; 1210 case NIS_IBFIRST: 1211 make_cookie = TRUE; 1212 break; 1213 case NIS_IBNEXT: 1214 make_cookie = TRUE; 1215 server_name = cookie_to_name(&req->ibr_cookie); 1216 if (server_name == 0) 1217 return (nis_make_error(NIS_NOMEMORY, 0, 1218 __stop_clock(CLOCK_CLIENT), 0, 0)); 1219 break; 1220 default: 1221 flags = MASTER_ONLY; 1222 timeout = NIS_GEN_TIMEOUT; 1223 break; 1224 } 1225 1226 __nis_init_call_state(&state); 1227 state.name = req->ibr_name; 1228 state.flags = flags; 1229 state.timeout.tv_sec = timeout; 1230 state.timeout.tv_usec = 0; 1231 state.parent_first = 1; 1232 state.server_name = server_name; 1233 1234 again: 1235 call_err = nis_call(&state, func, 1236 (xdrproc_t)xdr_ib_request, (char *)req, 1237 (xdrproc_t)xdr_nis_result, (char *)res); 1238 if (call_err == NIS_SUCCESS && res->status == NIS_NOTMASTER && 1239 times++ < MAX_NOTMASTER) 1240 goto again; 1241 res->aticks = state.aticks; 1242 if (make_cookie) { 1243 if (server_name == NULL) 1244 server_name = __nis_server_name(&state); 1245 if (server_name != NULL) 1246 name_to_cookie(server_name, res); 1247 } 1248 if (server_name != NULL) 1249 free(server_name); 1250 __nis_reset_call_state(&state); 1251 1252 if (obj) { 1253 obj->zo_name = oname; 1254 obj->zo_domain = odomain; 1255 obj->zo_owner = oowner; 1256 obj->zo_group = ogroup; 1257 } 1258 if (call_err) 1259 res->status = call_err; 1260 return (res); 1261 } 1262 1263 1264 /* 1265 * nis_add_entry() 1266 * 1267 * This function will add an entry to the named NIS table. 1268 */ 1269 nis_result * 1270 nis_add_entry( 1271 nis_name name, /* Table to use */ 1272 nis_object *obj, /* Entry object to add. */ 1273 uint_t flags) /* Semantic modification flags */ 1274 { 1275 nis_result *res; 1276 ib_request req; 1277 nis_error stat; 1278 1279 (void) __start_clock(CLOCK_CLIENT); 1280 __nis_CacheStart(); 1281 if (__nis_debug_calls) { 1282 (void) fprintf(__nis_debug_file, 1283 "nis_add_entry(%s, 0x%p, 0x%x\n", name?name:"(nil)", 1284 (void *)obj, flags); 1285 } 1286 stat = nis_get_request(name, obj, NULL, &req); 1287 if (stat != NIS_SUCCESS) { 1288 res = nis_make_error(stat, 0, __stop_clock(CLOCK_CLIENT), 0, 0); 1289 if (__nis_debug_calls) 1290 __nis_print_result(res); 1291 return (res); 1292 } 1293 req.ibr_flags = flags; 1294 res = nis_ibops(&req, NIS_IBADD); 1295 nis_free_request(&req); /* free up memory associated with request */ 1296 res->cticks += __stop_clock(CLOCK_CLIENT); 1297 if (__nis_debug_calls) 1298 __nis_print_result(res); 1299 return (res); 1300 } 1301 1302 /* 1303 * nis_remove_entry() 1304 * 1305 * This function will remove an entry to the named NIS table. 1306 */ 1307 nis_result * 1308 nis_remove_entry( 1309 nis_name name, /* Table to use */ 1310 nis_object *obj, /* Entry object to remove. */ 1311 uint_t flags) /* semantic modification flags */ 1312 { 1313 nis_result *res; 1314 ib_request req; 1315 nis_error stat; 1316 1317 (void) __start_clock(CLOCK_CLIENT); 1318 __nis_CacheStart(); 1319 if (__nis_debug_calls) { 1320 (void) fprintf(__nis_debug_file, 1321 "nis_remove_entry(%s, 0x%p, 0x%x)\n", name?name:"(nil)", 1322 (void *)obj, flags); 1323 } 1324 stat = nis_get_request(name, obj, NULL, &req); 1325 if (stat != NIS_SUCCESS) { 1326 res = nis_make_error(stat, 0, __stop_clock(CLOCK_CLIENT), 0, 0); 1327 if (__nis_debug_calls) 1328 __nis_print_result(res); 1329 return (res); 1330 } 1331 1332 req.ibr_flags = flags; 1333 res = nis_ibops(&req, NIS_IBREMOVE); 1334 nis_free_request(&req); /* free up memory associated with request */ 1335 res->cticks += __stop_clock(CLOCK_CLIENT); 1336 if (__nis_debug_calls) 1337 __nis_print_result(res); 1338 return (res); 1339 } 1340 1341 /* 1342 * nis_modify_entry() 1343 * 1344 * This function will modify an entry to the named NIS table. 1345 */ 1346 nis_result * 1347 nis_modify_entry( 1348 nis_name name, /* Table to use */ 1349 nis_object *obj, /* Entry object to modify. */ 1350 uint_t flags) /* Semantic modification flags */ 1351 { 1352 nis_result *res; 1353 ib_request req; 1354 nis_error stat; 1355 1356 (void) __start_clock(CLOCK_CLIENT); 1357 __nis_CacheStart(); 1358 if (__nis_debug_calls) { 1359 (void) fprintf(__nis_debug_file, 1360 "nis_modify_entry(%s, 0x%p, 0x%x)\n", name?name:"(nil)", 1361 (void *)obj, flags); 1362 } 1363 stat = nis_get_request(name, obj, NULL, &req); 1364 if (stat != NIS_SUCCESS) { 1365 res = nis_make_error(stat, 0, __stop_clock(CLOCK_CLIENT), 0, 0); 1366 if (__nis_debug_calls) 1367 __nis_print_result(res); 1368 return (res); 1369 } 1370 1371 req.ibr_flags = flags; 1372 res = nis_ibops(&req, NIS_IBMODIFY); 1373 nis_free_request(&req); /* free up memory associated with request */ 1374 res->cticks += __stop_clock(CLOCK_CLIENT); 1375 if (__nis_debug_calls) 1376 __nis_print_result(res); 1377 return (res); 1378 } 1379 1380 /* 1381 * nis_first_entry() 1382 * 1383 * This function will fetch the "first" entry in a table. 1384 */ 1385 nis_result * 1386 nis_first_entry(nis_name table) /* Table to read */ 1387 { 1388 nis_result *res; 1389 ib_request req; 1390 nis_error stat; 1391 1392 (void) __start_clock(CLOCK_CLIENT); 1393 __nis_CacheStart(); 1394 if (__nis_debug_calls) { 1395 (void) fprintf(__nis_debug_file, "nis_first_entry(%s)\n", 1396 table); 1397 } 1398 stat = nis_get_request(table, NULL, NULL, &req); 1399 if (stat != NIS_SUCCESS) { 1400 res = nis_make_error(stat, 0, __stop_clock(CLOCK_CLIENT), 0, 0); 1401 if (__nis_debug_calls) 1402 __nis_print_result(res); 1403 return (res); 1404 } 1405 1406 if (req.ibr_srch.ibr_srch_len) { 1407 res = nis_make_error(NIS_TOOMANYATTRS, 0, 1408 __stop_clock(CLOCK_CLIENT), 0, 0); 1409 if (__nis_debug_calls) 1410 __nis_print_result(res); 1411 return (res); 1412 } 1413 1414 res = nis_ibops(&req, NIS_IBFIRST); 1415 nis_free_request(&req); 1416 /* XXX at this point we should put the server in the cookie. */ 1417 /* free up memory associated with request */ 1418 res->cticks += __stop_clock(CLOCK_CLIENT); 1419 if (__nis_debug_calls) 1420 __nis_print_result(res); 1421 return (res); 1422 } 1423 1424 /* 1425 * nis_next_entry() 1426 * 1427 * This function will fetch the "first" entry in a table. 1428 */ 1429 nis_result * 1430 nis_next_entry( 1431 nis_name table, /* Table to read */ 1432 netobj *cookie) /* First/Next Cookie */ 1433 { 1434 nis_result *res; 1435 ib_request req; 1436 nis_error stat; 1437 1438 (void) __start_clock(CLOCK_CLIENT); 1439 __nis_CacheStart(); 1440 if (__nis_debug_calls) { 1441 (void) fprintf(__nis_debug_file, "nis_next_entry(%s, 0x%p)\n", 1442 table, (void *)cookie); 1443 } 1444 stat = nis_get_request(table, NULL, cookie, &req); 1445 if (stat != NIS_SUCCESS) { 1446 res = nis_make_error(stat, 0, __stop_clock(CLOCK_CLIENT), 0, 0); 1447 if (__nis_debug_calls) 1448 __nis_print_result(res); 1449 return (res); 1450 } 1451 1452 res = nis_ibops(&req, NIS_IBNEXT); 1453 /* free up memory associated with request */ 1454 nis_free_request(&req); 1455 res->cticks += __stop_clock(CLOCK_CLIENT); 1456 if (__nis_debug_calls) 1457 __nis_print_result(res); 1458 return (res); 1459 } 1460 1461 nis_result * 1462 nis_checkpoint(nis_name name) 1463 { 1464 nis_result *res; 1465 cp_result cpr; 1466 nis_error call_err; 1467 nis_call_state state; 1468 int times = 0; 1469 1470 (void) __start_clock(CLOCK_CLIENT); 1471 1472 res = calloc(1, sizeof (nis_result)); 1473 if (res == NULL) { 1474 (void) __stop_clock(CLOCK_CLIENT); 1475 return (NULL); 1476 } 1477 1478 __nis_init_call_state(&state); 1479 state.name = name; 1480 state.flags = MASTER_ONLY; 1481 again: 1482 call_err = nis_call(&state, NIS_CHECKPOINT, 1483 (xdrproc_t)xdr_nis_name, (char *)&name, 1484 (xdrproc_t)xdr_cp_result, (char *)&cpr); 1485 if (call_err == NIS_SUCCESS && cpr.cp_status == NIS_NOTMASTER && 1486 times++ < MAX_NOTMASTER) 1487 goto again; 1488 res->zticks = cpr.cp_zticks; 1489 res->dticks = cpr.cp_dticks; 1490 res->cticks = __stop_clock(CLOCK_CLIENT); 1491 res->aticks = state.aticks; 1492 __nis_reset_call_state(&state); 1493 if (call_err != NIS_SUCCESS) 1494 res->status = call_err; 1495 return (res); 1496 } 1497 1498 /* 1499 * nis_mkdir() 1500 * 1501 * This function is designed to allow a client to remotely create 1502 * a directory on a NIS server. When the server is contacted, it 1503 * will look up the directory object and determine if it should 1504 * really execute this command and if it should then everythings 1505 * cool. It returns an error if it can't create the directory. 1506 */ 1507 1508 nis_error 1509 nis_mkdir(nis_name name, nis_server *srv) 1510 { 1511 nis_error err; 1512 nis_error call_err; 1513 nis_call_state state; 1514 1515 __nis_CacheStart(); 1516 if (__nis_debug_calls) { 1517 (void) fprintf(__nis_debug_file, "nis_mkdir(%s, %s)\n", 1518 name, srv->name); 1519 } 1520 __nis_init_call_state(&state); 1521 state.srv = srv; 1522 state.nsrv = 1; 1523 call_err = nis_call(&state, NIS_MKDIR, 1524 (xdrproc_t)xdr_nis_name, (char *)&name, 1525 (xdrproc_t)xdr_nis_error, (char *)&err); 1526 __nis_reset_call_state(&state); 1527 if (call_err != NIS_SUCCESS) 1528 err = call_err; 1529 if (__nis_debug_calls) { 1530 (void) fprintf(__nis_debug_file, "status=%s\n", 1531 nis_sperrno(err)); 1532 } 1533 return (err); 1534 } 1535 1536 /* 1537 * nis_rmdir() 1538 * 1539 * This function is designed to allow a client to remotely remove 1540 * a directory on a NIS server. When the server is contacted, it 1541 * will look up the directory object and determine if it should 1542 * really execute this command and if it should then everythings 1543 * cool. It returns an error if it can't remove the directory. 1544 */ 1545 1546 nis_error 1547 nis_rmdir(nis_name name, nis_server *srv) 1548 { 1549 nis_error err; 1550 nis_error call_err; 1551 nis_call_state state; 1552 1553 __nis_CacheStart(); 1554 if (__nis_debug_calls) { 1555 (void) fprintf(__nis_debug_file, "nis_rmdir(%s, %s)\n", 1556 name, srv->name); 1557 } 1558 __nis_init_call_state(&state); 1559 state.srv = srv; 1560 state.nsrv = 1; 1561 call_err = nis_call(&state, NIS_RMDIR, 1562 (xdrproc_t)xdr_nis_name, (char *)&name, 1563 (xdrproc_t)xdr_nis_error, (char *)&err); 1564 __nis_reset_call_state(&state); 1565 if (call_err != NIS_SUCCESS) 1566 err = call_err; 1567 if (__nis_debug_calls) { 1568 (void) fprintf(__nis_debug_file, "status=%s\n", 1569 nis_sperrno(err)); 1570 } 1571 return (err); 1572 } 1573 1574 nis_error 1575 __nis_send_msg(nis_server *srv, int proc, xdrproc_t out, char *msg) 1576 { 1577 nis_error err; 1578 nis_error call_err; 1579 nis_call_state state; 1580 1581 __nis_CacheStart(); 1582 if (__nis_debug_calls) { 1583 (void) fprintf(__nis_debug_file, "nis_send_msg(%s, %d)\n", 1584 srv->name, proc); 1585 } 1586 __nis_init_call_state(&state); 1587 state.srv = srv; 1588 state.nsrv = 1; 1589 state.timeout.tv_sec = 0; 1590 state.timeout.tv_usec = 0; 1591 call_err = nis_call(&state, proc, 1592 out, msg, 1593 (xdrproc_t)0, (char *)0); 1594 __nis_reset_call_state(&state); 1595 if (call_err != NIS_SUCCESS) 1596 err = call_err; 1597 if (__nis_debug_calls) { 1598 (void) fprintf(__nis_debug_file, "status=%s\n", 1599 nis_sperrno(err)); 1600 } 1601 return (err); 1602 } 1603 1604 /* 1605 * A version of nis_list that takes a callback function, but doesn't do 1606 * callbacks over the wire (it gets the objects in the reply and then 1607 * feeds them to the callback function itself). 1608 */ 1609 1610 nis_result * 1611 __nis_list_localcb( 1612 nis_name name, /* list name like '[foo=bar].table.name' */ 1613 uint_t flags, /* Flags for the search */ 1614 int (*cback)(), /* Callback function. */ 1615 void *cbdata) /* Callback private data */ 1616 { 1617 nis_result *res; 1618 int no; 1619 nis_object *o; 1620 char *tab; 1621 int i; 1622 1623 /* 1624 * Do list without callbacks 1625 */ 1626 if ((res = nis_list(name, flags, 0, 0)) == 0) 1627 return (0); 1628 1629 /* 1630 * Run callback locally 1631 */ 1632 if (cback) 1633 switch (res->status) { 1634 case NIS_SUCCESS: 1635 case NIS_S_SUCCESS: 1636 /* 1637 * Always at least one object on success 1638 */ 1639 no = res->objects.objects_len; 1640 o = res->objects.objects_val; 1641 /* 1642 * Figure out the table name 1643 */ 1644 if (tab = strchr(name, ']')) { 1645 tab++; 1646 while (isspace(*tab) || (*tab == ',')) 1647 tab++; 1648 } else 1649 tab = name; 1650 /* 1651 * Run callback 1652 */ 1653 for (i = 0; i < no; i++) { 1654 if ((*cback)(tab, &(o[i]), cbdata)) 1655 break; 1656 } 1657 /* 1658 * Free objects 1659 */ 1660 for (i = 0; i < no; i++) 1661 xdr_free(xdr_nis_object, (char *)&(o[i])); 1662 free(res->objects.objects_val); 1663 /* 1664 * Fixup result 1665 */ 1666 res->objects.objects_len = 0; 1667 res->objects.objects_val = 0; 1668 res->status = NIS_CBRESULTS; 1669 break; 1670 }; 1671 1672 return (res); 1673 } 1674