1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 4 * Use is subject to license terms. 5 */ 6 7 #include <sys/stat.h> 8 #include <sys/types.h> 9 #include <unistd.h> 10 #include <fcntl.h> 11 #include <sys/mman.h> 12 #include <k5-int.h> 13 #include <stdlib.h> 14 #include <limits.h> 15 #include <syslog.h> 16 #include "kdb5.h" 17 #include "kdb_log.h" 18 #include "kdb5int.h" 19 20 #ifndef MAP_FAILED 21 #define MAP_FAILED ((void *)-1) 22 #endif 23 24 /* This module includes all the necessary functions that create and modify the 25 * Kerberos principal update and header logs. */ 26 27 #define getpagesize() sysconf(_SC_PAGESIZE) 28 29 static int pagesize = 0; 30 31 #define INIT_ULOG(ctx) \ 32 log_ctx = ctx->kdblog_context; \ 33 assert(log_ctx != NULL); \ 34 ulog = log_ctx->ulog; \ 35 assert(ulog != NULL) 36 37 /* Initialize context->kdblog_context if it does not yet exist, and return it. 38 * Return NULL on allocation failure. */ 39 static kdb_log_context * 40 create_log_context(krb5_context context) 41 { 42 kdb_log_context *log_ctx; 43 44 if (context->kdblog_context != NULL) 45 return context->kdblog_context; 46 log_ctx = calloc(1, sizeof(*log_ctx)); 47 if (log_ctx == NULL) 48 return NULL; 49 log_ctx->ulogfd = -1; 50 context->kdblog_context = log_ctx; 51 return log_ctx; 52 } 53 54 static inline krb5_boolean 55 time_equal(const kdbe_time_t *a, const kdbe_time_t *b) 56 { 57 return a->seconds == b->seconds && a->useconds == b->useconds; 58 } 59 60 static void 61 time_current(kdbe_time_t *out) 62 { 63 struct timeval timestamp; 64 65 (void)gettimeofday(×tamp, NULL); 66 out->seconds = timestamp.tv_sec; 67 out->useconds = timestamp.tv_usec; 68 } 69 70 /* Sync update entry to disk. */ 71 static void 72 sync_update(kdb_hlog_t *ulog, kdb_ent_header_t *upd) 73 { 74 unsigned long start, end, size; 75 76 if (!pagesize) 77 pagesize = getpagesize(); 78 79 start = (unsigned long)upd & ~(pagesize - 1); 80 81 end = ((unsigned long)upd + ulog->kdb_block + (pagesize - 1)) & 82 ~(pagesize - 1); 83 84 size = end - start; 85 if (msync((caddr_t)start, size, MS_SYNC)) { 86 /* Couldn't sync to disk, let's panic. */ 87 syslog(LOG_ERR, _("could not sync ulog update to disk")); 88 abort(); 89 } 90 } 91 92 /* Sync memory to disk for the update log header. */ 93 static void 94 sync_header(kdb_hlog_t *ulog) 95 { 96 if (!pagesize) 97 pagesize = getpagesize(); 98 99 if (msync((caddr_t)ulog, pagesize, MS_SYNC)) { 100 /* Couldn't sync to disk, let's panic. */ 101 syslog(LOG_ERR, _("could not sync ulog header to disk")); 102 abort(); 103 } 104 } 105 106 /* Sync memory to disk for the entire ulog. */ 107 static void 108 sync_ulog(kdb_hlog_t *ulog, uint32_t ulogentries) 109 { 110 size_t len; 111 112 if (!pagesize) 113 pagesize = getpagesize(); 114 115 len = (sizeof(kdb_hlog_t) + ulogentries * ulog->kdb_block + 116 (pagesize - 1)) & ~(pagesize - 1); 117 if (msync(ulog, len, MS_SYNC)) { 118 /* Couldn't sync to disk, let's panic. */ 119 syslog(LOG_ERR, _("could not sync the whole ulog to disk")); 120 abort(); 121 } 122 } 123 124 /* Return true if the ulog entry for sno matches sno and timestamp. */ 125 static krb5_boolean 126 check_sno(kdb_log_context *log_ctx, kdb_sno_t sno, 127 const kdbe_time_t *timestamp) 128 { 129 unsigned int indx = (sno - 1) % log_ctx->ulogentries; 130 kdb_ent_header_t *ent = ulog_index(log_ctx->ulog, indx); 131 132 return ent->kdb_entry_sno == sno && time_equal(&ent->kdb_time, timestamp); 133 } 134 135 /* 136 * Check last against our ulog and determine whether it is up to date 137 * (UPDATE_NIL), so far out of date that a full dump is required 138 * (UPDATE_FULL_RESYNC_NEEDED), or okay to update with ulog entries 139 * (UPDATE_OK). 140 */ 141 static update_status_t 142 get_sno_status(kdb_log_context *log_ctx, const kdb_last_t *last) 143 { 144 kdb_hlog_t *ulog = log_ctx->ulog; 145 146 /* If last matches the ulog's last serial number and time exactly, it are 147 * up to date even if the ulog is empty. */ 148 if (last->last_sno == ulog->kdb_last_sno && 149 time_equal(&last->last_time, &ulog->kdb_last_time)) 150 return UPDATE_NIL; 151 152 /* If our ulog is empty or does not contain last_sno, a full resync is 153 * required. */ 154 if (ulog->kdb_num == 0 || last->last_sno > ulog->kdb_last_sno || 155 last->last_sno < ulog->kdb_first_sno) 156 return UPDATE_FULL_RESYNC_NEEDED; 157 158 /* If the timestamp in our ulog entry does not match last, then sno was 159 * reused and a full resync is required. */ 160 if (!check_sno(log_ctx, last->last_sno, &last->last_time)) 161 return UPDATE_FULL_RESYNC_NEEDED; 162 163 /* last is not fully up to date, but can be updated using our ulog. */ 164 return UPDATE_OK; 165 } 166 167 /* Extend update log file. */ 168 static krb5_error_code 169 extend_file_to(int fd, unsigned int new_size) 170 { 171 off_t current_offset; 172 static const char zero[512]; 173 ssize_t wrote_size; 174 size_t write_size; 175 176 current_offset = lseek(fd, 0, SEEK_END); 177 if (current_offset < 0) 178 return errno; 179 if (new_size > INT_MAX) 180 return EINVAL; 181 while (current_offset < (off_t)new_size) { 182 write_size = new_size - current_offset; 183 if (write_size > 512) 184 write_size = 512; 185 wrote_size = write(fd, zero, write_size); 186 if (wrote_size < 0) 187 return errno; 188 if (wrote_size == 0) 189 return EINVAL; 190 current_offset += wrote_size; 191 write_size = new_size - current_offset; 192 } 193 return 0; 194 } 195 196 /* Resize the array elements of ulog to be at least as large as recsize. Move 197 * the existing elements into the proper offsets for the new block size. */ 198 static krb5_error_code 199 resize(kdb_hlog_t *ulog, uint32_t ulogentries, int ulogfd, 200 unsigned int recsize, const kdb_incr_update_t *upd) 201 { 202 size_t old_block = ulog->kdb_block, new_block, new_size; 203 krb5_error_code retval; 204 uint8_t *old_ent, *new_ent; 205 uint32_t i; 206 207 if (ulog == NULL) 208 return KRB5_LOG_ERROR; 209 210 new_size = sizeof(kdb_hlog_t); 211 new_block = (recsize / ULOG_BLOCK) + 1; 212 new_block *= ULOG_BLOCK; 213 new_size += ulogentries * new_block; 214 215 if (new_block > UINT16_MAX) { 216 syslog(LOG_ERR, _("ulog overflow caused by principal %.*s"), 217 upd->kdb_princ_name.utf8str_t_len, 218 upd->kdb_princ_name.utf8str_t_val); 219 return KRB5_LOG_ERROR; 220 } 221 if (new_size > MAXLOGLEN) 222 return KRB5_LOG_ERROR; 223 224 /* Expand log considering new block size. */ 225 retval = extend_file_to(ulogfd, new_size); 226 if (retval) 227 return retval; 228 229 /* Copy each record into its new location and zero out the unused areas. 230 * The area is overlapping, so we have to iterate backwards. */ 231 for (i = ulogentries; i > 0; i--) { 232 old_ent = ulog_record_ptr(ulog, i - 1, old_block); 233 new_ent = ulog_record_ptr(ulog, i - 1, new_block); 234 memmove(new_ent, old_ent, old_block); 235 memset(new_ent + old_block, 0, new_block - old_block); 236 } 237 238 syslog(LOG_INFO, _("ulog block size has been resized from %lu to %lu"), 239 (unsigned long)old_block, (unsigned long)new_block); 240 ulog->kdb_block = new_block; 241 sync_ulog(ulog, ulogentries); 242 return 0; 243 } 244 245 /* Set the ulog to contain only a dummy entry with the given serial number and 246 * timestamp. */ 247 static void 248 set_dummy(kdb_log_context *log_ctx, kdb_sno_t sno, const kdbe_time_t *kdb_time) 249 { 250 kdb_hlog_t *ulog = log_ctx->ulog; 251 kdb_ent_header_t *ent = ulog_index(ulog, (sno - 1) % log_ctx->ulogentries); 252 253 memset(ent, 0, sizeof(*ent)); 254 ent->kdb_umagic = KDB_ULOG_MAGIC; 255 ent->kdb_entry_sno = sno; 256 ent->kdb_time = *kdb_time; 257 sync_update(ulog, ent); 258 259 ulog->kdb_num = 1; 260 ulog->kdb_first_sno = ulog->kdb_last_sno = sno; 261 ulog->kdb_first_time = ulog->kdb_last_time = *kdb_time; 262 } 263 264 /* Reinitialize the ulog header, starting from sno 1 with the current time. */ 265 static void 266 reset_ulog(kdb_log_context *log_ctx) 267 { 268 kdbe_time_t kdb_time; 269 kdb_hlog_t *ulog = log_ctx->ulog; 270 271 memset(ulog, 0, sizeof(*ulog)); 272 ulog->kdb_hmagic = KDB_ULOG_HDR_MAGIC; 273 ulog->db_version_num = KDB_VERSION; 274 ulog->kdb_block = ULOG_BLOCK; 275 276 /* Create a dummy entry to remember the timestamp for downstreams. */ 277 time_current(&kdb_time); 278 set_dummy(log_ctx, 1, &kdb_time); 279 ulog->kdb_state = KDB_STABLE; 280 sync_header(ulog); 281 } 282 283 /* 284 * If any database operations will be invoked while the ulog lock is held, the 285 * caller must explicitly lock the database before locking the ulog, or 286 * deadlock may result. 287 */ 288 static krb5_error_code 289 lock_ulog(krb5_context context, int mode) 290 { 291 kdb_log_context *log_ctx = NULL; 292 kdb_hlog_t *ulog = NULL; 293 294 INIT_ULOG(context); 295 return krb5_lock_file(context, log_ctx->ulogfd, mode); 296 } 297 298 static void 299 unlock_ulog(krb5_context context) 300 { 301 (void)lock_ulog(context, KRB5_LOCKMODE_UNLOCK); 302 } 303 304 /* 305 * Add an update to the log. The update's kdb_entry_sno and kdb_time fields 306 * must already be set. The layout of the update log looks like: 307 * 308 * header log -> [ update header -> xdr(kdb_incr_update_t) ], ... 309 */ 310 static krb5_error_code 311 store_update(kdb_log_context *log_ctx, kdb_incr_update_t *upd) 312 { 313 XDR xdrs; 314 kdb_ent_header_t *indx_log; 315 unsigned int i, recsize; 316 unsigned long upd_size; 317 krb5_error_code retval; 318 kdb_hlog_t *ulog = log_ctx->ulog; 319 uint32_t ulogentries = log_ctx->ulogentries; 320 321 upd_size = xdr_sizeof((xdrproc_t)xdr_kdb_incr_update_t, upd); 322 323 recsize = sizeof(kdb_ent_header_t) + upd_size; 324 325 if (recsize > ulog->kdb_block) { 326 retval = resize(ulog, ulogentries, log_ctx->ulogfd, recsize, upd); 327 if (retval) 328 return retval; 329 } 330 331 ulog->kdb_state = KDB_UNSTABLE; 332 333 i = (upd->kdb_entry_sno - 1) % ulogentries; 334 indx_log = ulog_index(ulog, i); 335 336 memset(indx_log, 0, ulog->kdb_block); 337 indx_log->kdb_umagic = KDB_ULOG_MAGIC; 338 indx_log->kdb_entry_size = upd_size; 339 indx_log->kdb_entry_sno = upd->kdb_entry_sno; 340 indx_log->kdb_time = upd->kdb_time; 341 indx_log->kdb_commit = FALSE; 342 343 xdrmem_create(&xdrs, (char *)indx_log->entry_data, 344 indx_log->kdb_entry_size, XDR_ENCODE); 345 if (!xdr_kdb_incr_update_t(&xdrs, upd)) 346 return KRB5_LOG_CONV; 347 348 indx_log->kdb_commit = TRUE; 349 sync_update(ulog, indx_log); 350 351 /* Modify the ulog header to reflect the new update. */ 352 ulog->kdb_last_sno = upd->kdb_entry_sno; 353 ulog->kdb_last_time = upd->kdb_time; 354 if (ulog->kdb_num == 0) { 355 /* We should only see this in old ulogs. */ 356 ulog->kdb_num = 1; 357 ulog->kdb_first_sno = upd->kdb_entry_sno; 358 ulog->kdb_first_time = upd->kdb_time; 359 } else if (ulog->kdb_num < ulogentries) { 360 ulog->kdb_num++; 361 } else { 362 /* We are circling; set kdb_first_sno and time to the next update. */ 363 i = upd->kdb_entry_sno % ulogentries; 364 indx_log = ulog_index(ulog, i); 365 ulog->kdb_first_sno = indx_log->kdb_entry_sno; 366 ulog->kdb_first_time = indx_log->kdb_time; 367 } 368 369 ulog->kdb_state = KDB_STABLE; 370 sync_header(ulog); 371 return 0; 372 } 373 374 /* Add an entry to the update log. */ 375 krb5_error_code 376 ulog_add_update(krb5_context context, kdb_incr_update_t *upd) 377 { 378 krb5_error_code ret; 379 kdb_log_context *log_ctx; 380 kdb_hlog_t *ulog; 381 382 INIT_ULOG(context); 383 ret = lock_ulog(context, KRB5_LOCKMODE_EXCLUSIVE); 384 if (ret) 385 return ret; 386 387 /* If we have reached the last possible serial number, reinitialize the 388 * ulog and start over. Replicas will do a full resync. */ 389 if (ulog->kdb_last_sno == (kdb_sno_t)-1) 390 reset_ulog(log_ctx); 391 392 upd->kdb_entry_sno = ulog->kdb_last_sno + 1; 393 time_current(&upd->kdb_time); 394 ret = store_update(log_ctx, upd); 395 unlock_ulog(context); 396 return ret; 397 } 398 399 /* Used by the replica to update its hash db from the incr update log. */ 400 krb5_error_code 401 ulog_replay(krb5_context context, kdb_incr_result_t *incr_ret, char **db_args) 402 { 403 krb5_db_entry *entry = NULL; 404 kdb_incr_update_t *upd = NULL, *fupd; 405 int i, no_of_updates; 406 krb5_error_code retval; 407 krb5_principal dbprinc; 408 char *dbprincstr; 409 kdb_log_context *log_ctx; 410 kdb_hlog_t *ulog = NULL; 411 412 INIT_ULOG(context); 413 414 retval = krb5_db_open(context, db_args, 415 KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN); 416 if (retval) 417 return retval; 418 419 no_of_updates = incr_ret->updates.kdb_ulog_t_len; 420 upd = incr_ret->updates.kdb_ulog_t_val; 421 fupd = upd; 422 423 for (i = 0; i < no_of_updates; i++) { 424 if (!upd->kdb_commit) 425 continue; 426 427 /* Replay this update in the database. */ 428 if (upd->kdb_deleted) { 429 dbprincstr = k5memdup0(upd->kdb_princ_name.utf8str_t_val, 430 upd->kdb_princ_name.utf8str_t_len, &retval); 431 if (dbprincstr == NULL) 432 goto cleanup; 433 434 retval = krb5_parse_name(context, dbprincstr, &dbprinc); 435 free(dbprincstr); 436 if (retval) 437 goto cleanup; 438 439 retval = krb5int_delete_principal_no_log(context, dbprinc); 440 krb5_free_principal(context, dbprinc); 441 if (retval == KRB5_KDB_NOENTRY) 442 retval = 0; 443 if (retval) 444 goto cleanup; 445 } else { 446 retval = ulog_conv_2dbentry(context, &entry, upd); 447 if (retval) 448 goto cleanup; 449 450 retval = krb5int_put_principal_no_log(context, entry); 451 krb5_db_free_principal(context, entry); 452 if (retval) 453 goto cleanup; 454 } 455 456 retval = lock_ulog(context, KRB5_LOCKMODE_EXCLUSIVE); 457 if (retval) 458 goto cleanup; 459 460 /* If (unexpectedly) this update does not follow the last one we 461 * stored, discard any previous ulog state. */ 462 if (ulog->kdb_num != 0 && upd->kdb_entry_sno != ulog->kdb_last_sno + 1) 463 reset_ulog(log_ctx); 464 465 /* Store this update in the ulog for any downstream KDCs. */ 466 retval = store_update(log_ctx, upd); 467 unlock_ulog(context); 468 if (retval) 469 goto cleanup; 470 471 upd++; 472 } 473 474 cleanup: 475 if (retval) 476 (void)ulog_init_header(context); 477 if (fupd) 478 ulog_free_entries(fupd, no_of_updates); 479 return retval; 480 } 481 482 /* Reinitialize the log header. */ 483 krb5_error_code 484 ulog_init_header(krb5_context context) 485 { 486 krb5_error_code ret; 487 kdb_log_context *log_ctx; 488 kdb_hlog_t *ulog; 489 490 INIT_ULOG(context); 491 ret = lock_ulog(context, KRB5_LOCKMODE_EXCLUSIVE); 492 if (ret) 493 return ret; 494 reset_ulog(log_ctx); 495 unlock_ulog(context); 496 return 0; 497 } 498 499 /* Map the log file to memory for performance and simplicity. */ 500 krb5_error_code 501 ulog_map(krb5_context context, const char *logname, uint32_t ulogentries) 502 { 503 struct stat st; 504 krb5_error_code retval; 505 uint32_t filesize; 506 kdb_log_context *log_ctx; 507 kdb_hlog_t *ulog = NULL; 508 krb5_boolean locked = FALSE; 509 510 log_ctx = create_log_context(context); 511 if (log_ctx == NULL) 512 return ENOMEM; 513 514 if (stat(logname, &st) == -1) { 515 log_ctx->ulogfd = open(logname, O_RDWR | O_CREAT, 0600); 516 if (log_ctx->ulogfd == -1) { 517 retval = errno; 518 goto cleanup; 519 } 520 521 filesize = sizeof(kdb_hlog_t) + ulogentries * ULOG_BLOCK; 522 retval = extend_file_to(log_ctx->ulogfd, filesize); 523 if (retval) 524 goto cleanup; 525 } else { 526 log_ctx->ulogfd = open(logname, O_RDWR, 0600); 527 if (log_ctx->ulogfd == -1) { 528 retval = errno; 529 goto cleanup; 530 } 531 } 532 533 ulog = mmap(0, MAXLOGLEN, PROT_READ | PROT_WRITE, MAP_SHARED, 534 log_ctx->ulogfd, 0); 535 if (ulog == MAP_FAILED) { 536 retval = errno; 537 goto cleanup; 538 } 539 log_ctx->ulog = ulog; 540 log_ctx->ulogentries = ulogentries; 541 542 retval = lock_ulog(context, KRB5_LOCKMODE_EXCLUSIVE); 543 if (retval) 544 goto cleanup; 545 locked = TRUE; 546 547 if (ulog->kdb_hmagic != KDB_ULOG_HDR_MAGIC) { 548 if (ulog->kdb_hmagic != 0) { 549 retval = KRB5_LOG_CORRUPT; 550 goto cleanup; 551 } 552 reset_ulog(log_ctx); 553 } 554 555 /* Reinit ulog if ulogentries changed such that we have too many entries or 556 * our first or last entry was written to the wrong location. */ 557 if (ulog->kdb_num != 0 && 558 (ulog->kdb_num > ulogentries || 559 !check_sno(log_ctx, ulog->kdb_first_sno, &ulog->kdb_first_time) || 560 !check_sno(log_ctx, ulog->kdb_last_sno, &ulog->kdb_last_time))) 561 reset_ulog(log_ctx); 562 563 if (ulog->kdb_num != ulogentries) { 564 /* Expand the ulog file if it isn't big enough. */ 565 filesize = sizeof(kdb_hlog_t) + ulogentries * ulog->kdb_block; 566 retval = extend_file_to(log_ctx->ulogfd, filesize); 567 if (retval) 568 goto cleanup; 569 } 570 571 cleanup: 572 if (locked) 573 unlock_ulog(context); 574 if (retval) 575 ulog_fini(context); 576 return retval; 577 } 578 579 /* Get the last set of updates seen, (last+1) to n is returned. */ 580 krb5_error_code 581 ulog_get_entries(krb5_context context, const kdb_last_t *last, 582 kdb_incr_result_t *ulog_handle) 583 { 584 XDR xdrs; 585 kdb_ent_header_t *indx_log; 586 kdb_incr_update_t *upd; 587 unsigned int indx, count; 588 uint32_t sno; 589 krb5_error_code retval; 590 kdb_log_context *log_ctx; 591 kdb_hlog_t *ulog = NULL; 592 uint32_t ulogentries; 593 594 INIT_ULOG(context); 595 ulogentries = log_ctx->ulogentries; 596 597 retval = lock_ulog(context, KRB5_LOCKMODE_SHARED); 598 if (retval) 599 return retval; 600 601 /* If another process terminated mid-update, reset the ulog and force full 602 * resyncs. */ 603 if (ulog->kdb_state != KDB_STABLE) 604 reset_ulog(log_ctx); 605 606 ulog_handle->ret = get_sno_status(log_ctx, last); 607 if (ulog_handle->ret != UPDATE_OK) 608 goto cleanup; 609 610 sno = last->last_sno; 611 count = ulog->kdb_last_sno - sno; 612 upd = calloc(count, sizeof(kdb_incr_update_t)); 613 if (upd == NULL) { 614 ulog_handle->ret = UPDATE_ERROR; 615 retval = ENOMEM; 616 goto cleanup; 617 } 618 ulog_handle->updates.kdb_ulog_t_val = upd; 619 620 for (; sno < ulog->kdb_last_sno; sno++) { 621 indx = sno % ulogentries; 622 indx_log = ulog_index(ulog, indx); 623 624 memset(upd, 0, sizeof(kdb_incr_update_t)); 625 xdrmem_create(&xdrs, (char *)indx_log->entry_data, 626 indx_log->kdb_entry_size, XDR_DECODE); 627 if (!xdr_kdb_incr_update_t(&xdrs, upd)) { 628 ulog_handle->ret = UPDATE_ERROR; 629 retval = KRB5_LOG_CONV; 630 goto cleanup; 631 } 632 633 /* Mark commitment since we didn't want to decode and encode the incr 634 * update record the first time. */ 635 upd->kdb_commit = indx_log->kdb_commit; 636 upd++; 637 } 638 639 ulog_handle->updates.kdb_ulog_t_len = count; 640 641 ulog_handle->lastentry.last_sno = ulog->kdb_last_sno; 642 ulog_handle->lastentry.last_time.seconds = ulog->kdb_last_time.seconds; 643 ulog_handle->lastentry.last_time.useconds = ulog->kdb_last_time.useconds; 644 ulog_handle->ret = UPDATE_OK; 645 646 cleanup: 647 unlock_ulog(context); 648 return retval; 649 } 650 651 krb5_error_code 652 ulog_set_role(krb5_context ctx, iprop_role role) 653 { 654 if (create_log_context(ctx) == NULL) 655 return ENOMEM; 656 ctx->kdblog_context->iproprole = role; 657 return 0; 658 } 659 660 update_status_t 661 ulog_get_sno_status(krb5_context context, const kdb_last_t *last) 662 { 663 update_status_t status; 664 665 if (lock_ulog(context, KRB5_LOCKMODE_SHARED) != 0) 666 return UPDATE_ERROR; 667 status = get_sno_status(context->kdblog_context, last); 668 unlock_ulog(context); 669 return status; 670 } 671 672 krb5_error_code 673 ulog_get_last(krb5_context context, kdb_last_t *last_out) 674 { 675 krb5_error_code ret; 676 kdb_log_context *log_ctx; 677 kdb_hlog_t *ulog; 678 679 INIT_ULOG(context); 680 ret = lock_ulog(context, KRB5_LOCKMODE_SHARED); 681 if (ret) 682 return ret; 683 last_out->last_sno = log_ctx->ulog->kdb_last_sno; 684 last_out->last_time = log_ctx->ulog->kdb_last_time; 685 unlock_ulog(context); 686 return 0; 687 } 688 689 krb5_error_code 690 ulog_set_last(krb5_context context, const kdb_last_t *last) 691 { 692 krb5_error_code ret; 693 kdb_log_context *log_ctx; 694 kdb_hlog_t *ulog; 695 696 INIT_ULOG(context); 697 ret = lock_ulog(context, KRB5_LOCKMODE_EXCLUSIVE); 698 if (ret) 699 return ret; 700 701 set_dummy(log_ctx, last->last_sno, &last->last_time); 702 sync_header(ulog); 703 unlock_ulog(context); 704 return 0; 705 } 706 707 void 708 ulog_fini(krb5_context context) 709 { 710 kdb_log_context *log_ctx = context->kdblog_context; 711 712 if (log_ctx == NULL) 713 return; 714 if (log_ctx->ulog != NULL) 715 munmap(log_ctx->ulog, MAXLOGLEN); 716 if (log_ctx->ulogfd != -1) 717 close(log_ctx->ulogfd); 718 free(log_ctx); 719 context->kdblog_context = NULL; 720 } 721