#ifndef lint static char sccsid[] = "@(#)prot_proc.c 1.1 94/10/31 SMI"; #endif /* * Copyright (c) 1988 by Sun Microsystems, Inc. */ /* * prot_proc.c * consists all local, remote, and continuation routines: * local_xxx, remote_xxx, and cont_xxx. */ #include #include #include #include #include "prot_lock.h" #include "lockf.h" remote_result nlm_result; /* local nlm result */ remote_result *nlm_resp = &nlm_result; /* ptr to klm result */ remote_result *get_res(); remote_result *remote_cancel(); remote_result *remote_lock(); remote_result *remote_test(); remote_result *remote_unlock(); remote_result *local_test(); remote_result *local_lock(); remote_result *local_unlock(); remote_result *cont_test(); remote_result *cont_lock(); remote_result *cont_unlock(); remote_result *cont_cancel(); remote_result *cont_reclaim(); extern msg_entry *retransmitted(); extern int debug, errno; extern msg_entry *msg_q; extern bool_t obj_cmp(); extern int obj_copy(); extern struct reclock *find_block_req(); extern void dequeue_block_req(); extern char *xmalloc(); extern struct fd_table *fd_table; static void remove_fd(); static int used_fd = 0; static int lastfd = 0; print_fdtable() { struct fd_table *t; int i, j; printf("In print_fdtable()....used_fd=%d, lastfd=%d\n", used_fd, lastfd); for (t = fd_table, j = 0; j <= lastfd; t++, j++) { if (t->fd) { printf("ID=%d\n", t->fd); for (i = 0; i < t->fh.n_len; i++) { printf("%02x", (t->fh.n_bytes[i] & 0xff)); } printf("\n"); } } } /* * Remove the fd_table entry for the given file descriptor and put it on * the free list. */ static void remove_fd(fd) int fd; { struct fd_table *t; int i; if (debug) printf("remove_fd(%d) ...\n", fd); if (fd == 0) { printf("rpc.lockd: trying to close fd 0.\n"); return; } t = &fd_table[fd]; if (t->fh.n_len) { if (debug) { for (i = 0; i < t->fh.n_len; i++) { printf("%02x", (t->fh.n_bytes[i] & 0xff)); } printf("\n"); } bzero(t->fh.n_bytes, sizeof (t->fh.n_bytes)); xfree(&(t->fh.n_bytes)); t->fh.n_len = 0; t->fd = 0; used_fd--; if (fd == lastfd) lastfd--; } if (debug) print_fdtable(); } int get_fd(a) struct reclock *a; { struct fd_table *t; int fd, cmd, i; int j; struct { char *fh; int filemode; } fa; if (debug) { printf("enter get_fd ....\n"); } for (t = fd_table, j = 0; j <= lastfd; t++, j++) { if (t->fd && obj_cmp(&(t->fh), &(a->lck.fh))) { if (debug) { printf("Found fd entry : a = "); for (i = 0; i < a->lck.fh.n_len; i++) { printf("%02x", (a->lck.fh.n_bytes[i] & 0xff)); } printf("\nfd_table->fh = "); for (i = 0; i < 32; i++) { printf("%02x", (t->fh.n_bytes[i] & 0xff)); } printf("\n"); } return (t->fd); } } /* * convert fh to fd */ cmd = F_CNVT; fa.fh = a->lck.fh.n_bytes; if (debug) { printf("Convert fd entry : "); for (i = 0; i < a->lck.fh.n_len; i++) { printf("%02x", (a->lck.fh.n_bytes[i] & 0xff)); } printf("\n"); } fa.filemode = O_RDWR; { int err = 0; if ((fd = fcntl(0, cmd, &fa)) == -1) { err = errno; } if (err == EROFS && a->exclusive == 0) { fa.filemode = O_RDONLY; fa.fh = a->lck.fh.n_bytes; err = 0; if ((fd = fcntl(0, cmd, &fa)) == -1) { err = errno; } } if (err != 0) { errno = err; perror("fcntl"); printf("rpc.lockd: unable to do cnvt.\n"); if ((errno == ENOLCK)||(errno == ESTALE)) return (-1); else return (-2); } } if (lastfd < fd) lastfd = fd; t = &fd_table[fd]; obj_copy(&t->fh, &a->lck.fh); t->fd = fd; used_fd++; if (debug) print_fdtable(); return (fd); } /* * If *grant_lock_flag is FALSE: make a local (server-side) lock request * for the given lock. Sets *grant_lock_flag to TRUE if the request causes * one or more blocked locks to be granted. Returns the status of the * request (success, denied, etc.). Note: if *grant_lock_flag gets set to * TRUE, the caller is expected to call again with *grant_lock_flag left as * TRUE. It is important that the caller not process any additional RPC * requests before making this second call (e.g., to avoid possible file * descriptor leaks). * * If *grant_lock_flag is TRUE: query the kernel for all newly granted * locks and inform the appropriate clients of the locks' new status. * Returns NULL. */ remote_result * local_lock(a, grant_lock_flag) struct reclock *a; int *grant_lock_flag; { int cmd; remote_result *remote_grant(); struct reclock grant_lock; static struct eflock ld; static int fd; /* if there r locks to be granted, keep calling fcntl() to get */ /* info on these locks & send each of these granted locks to */ /* the respective lock mgr, NOTICE that only locks that r held */ /* by lockmgr is being replied here, granting of local blocking */ /* locks is handled by the kernel. */ if (*grant_lock_flag) { if (debug) printf("granting locks...\n"); ld.l_type = F_RDLCK; ld.l_xxx = GRANT_LOCK_FLAG; while (fcntl(fd, F_RSETLKW, &ld) != -1) { grant_lock.block = TRUE; grant_lock.exclusive = FALSE; grant_lock.lck.lox.base = ld.l_start; grant_lock.lck.lox.length = ld.l_len; grant_lock.lck.lox.pid = ld.l_pid; grant_lock.lck.lox.rpid = ld.l_rpid; grant_lock.lck.lox.rsys = ld.l_rsys; grant_lock.lck.lox.type = ld.l_type; grant_lock.rel = 0; /* send this granted lock to the respective remote */ /* lock mgr */ remote_grant(&grant_lock, MSG); /* no more granted locks, quit */ if (ld.l_xxx < 0) break; ld.l_type = F_RDLCK; ld.l_xxx = GRANT_LOCK_FLAG; } *grant_lock_flag = FALSE; return(NULL); } /* * convert fh to fd */ if ((fd = get_fd(a)) < 0) { if (fd == -1) nlm_resp->lstat = nlm_denied_nolocks; else nlm_resp->lstat = denied; return (nlm_resp); } /* * set the lock */ if (debug) { printf("enter local_lock...FD=%d\n", fd); pr_lock(a); (void) fflush(stdout); } if (a->block) cmd = F_RSETLKW; else cmd = F_RSETLK; if (a->exclusive) ld.l_type = F_WRLCK; else ld.l_type = F_RDLCK; ld.l_whence = 0; ld.l_start = a->lck.lox.base; ld.l_len = a->lck.lox.length; ld.l_pid = a->lck.lox.pid; ld.l_rpid = a->lck.lox.rpid; ld.l_rsys = a->lck.lox.rsys; ld.l_xxx = 0; if (debug) { printf("ld.l_start=%d ld.l_len=%d ld.l_rpid=%d ld.l_rsys=%x\n", ld.l_start, ld.l_len, ld.l_rpid, ld.l_rsys); } if (fcntl(fd, cmd, &ld) == -1) { if (errno == EINTR) { nlm_resp->lstat = blocking; a->w_flag = 1; } else if (errno == EDEADLK) { perror("fcntl"); nlm_resp->lstat = deadlck; a->w_flag = 0; } else if (errno == ENOLCK) { perror("fcntl"); printf("rpc.lockd: out of lock.\n"); nlm_resp->lstat = nlm_denied_nolocks; } else if (((cmd == F_SETLK) || (cmd == F_RSETLK)) && (errno == EACCES)) { nlm_resp->lstat = denied; } else { perror("fcntl"); printf("rpc.lockd: unable to set a lock. \n"); nlm_resp->lstat = denied; } } else nlm_resp->lstat = nlm_granted; if (ld.l_xxx == GRANT_LOCK_FLAG) *grant_lock_flag = TRUE; return (nlm_resp); } /* * choice == RPC; rpc calls to remote; * choice == MSG; msg passing calls to remote; */ remote_result * remote_lock(a, choice) struct reclock *a; int choice; { if (debug) { printf("enter remote_lock\n"); pr_lock(a); (void) fflush(stdout); } if (choice == MSG) { /* msg passing */ if (nlm_call(NLM_LOCK_MSG, a, 0) == -1) a->rel = 1; } return(NULL); } /* * If *grant_lock_flag is FALSE: make a local (server-side) unlock request * for the given lock. Sets *grant_lock_flag to TRUE if the request causes * one or more blocked locks to be granted. Returns the status of the * request (success, denied, etc.). If this lock is the last remote lock * for the file, the file is closed and its descriptor is removed from the * descriptor table. Note: if *grant_lock_flag gets set to TRUE, the * caller is expected to call again with *grant_lock_flag left as TRUE. It * is important that the caller not process any additional RPC requests * before making this second call (e.g., to avoid possible file descriptor * leaks). * * If *grant_lock_flag is TRUE: query the kernel for all newly granted * locks and inform the appropriate clients of the locks' new status. * Returns NULL. */ remote_result * local_unlock(a, grant_lock_flag) struct reclock *a; int *grant_lock_flag; { int cmd; static struct eflock ld; remote_result *remote_grant(); struct reclock grant_lock; static int fd; if (debug) printf("enter local_unlock...................\n"); /* if there r locks to be granted, keep calling fcntl() to get */ /* info on these locks & send each of these granted locks to */ /* the respective lock mgr, NOTICE that only locks that r held */ /* by lockmgr is being replied here, granting of local blocking */ /* locks is handled by the kernel. */ if (*grant_lock_flag) { if (debug) printf("granting locks...\n"); ld.l_type = F_UNLCK; ld.l_xxx = GRANT_LOCK_FLAG; while (fcntl(fd, F_RSETLKW, &ld) != -1) { grant_lock.block = TRUE; grant_lock.exclusive = TRUE; grant_lock.lck.lox.base = ld.l_start; grant_lock.lck.lox.length = ld.l_len; grant_lock.lck.lox.pid = ld.l_pid; grant_lock.lck.lox.rpid = ld.l_rpid; grant_lock.lck.lox.rsys = ld.l_rsys; grant_lock.lck.lox.type = ld.l_type; grant_lock.rel = 0; /* send this granted lock to the respective remote */ /* lock mgr */ remote_grant(&grant_lock, MSG); /* no more granted locks, quit */ if (ld.l_xxx < 0) break; ld.l_type = F_UNLCK; ld.l_xxx = GRANT_LOCK_FLAG; } *grant_lock_flag = FALSE; return(NULL); } *grant_lock_flag = FALSE; /* * convert fh to fd */ if ((fd = get_fd(a)) < 0) { if (fd == -1) nlm_resp->lstat = nlm_denied_nolocks; else nlm_resp->lstat = nlm_denied; return (nlm_resp); } /* * set the lock */ if (a->block) cmd = F_RSETLKW; else cmd = F_RSETLK; ld.l_type = F_UNLCK; ld.l_whence = 0; ld.l_start = a->lck.lox.base; ld.l_len = a->lck.lox.length; ld.l_pid = a->lck.lox.pid; ld.l_rpid = a->lck.lox.rpid; ld.l_rsys = a->lck.lox.rsys; ld.l_xxx = 0; if (debug) { printf("ld.l_start=%d ld.l_len=%d ld.l_pid=%d ld.l_rpid=%d ld.l_rsys=%x\n", ld.l_start, ld.l_len, ld.l_pid, ld.l_rpid, ld.l_rsys); } if (fcntl(fd, cmd, &ld) == -1) { if (errno == EINTR) { nlm_resp->lstat = blocking; a->w_flag = 1; } else if (errno == ENOLCK) { perror("fcntl"); printf("rpc.lockd: out of lock.\n"); nlm_resp->lstat = nlm_denied_nolocks; } else { perror("fcntl"); printf("rpc.lockd: unable to unlock a lock. \n"); nlm_resp->lstat = nlm_denied; } if (ld.l_xxx == GRANT_LOCK_FLAG) *grant_lock_flag = TRUE; } else { /* if fcntl() returned w/ l_xxx >0, means kernel tells us */ /* that this unlock request results in some locks to be */ /* granted, & we need to call fcntl() again to get these */ /* granted locks. we call fcntl() again in the beginning */ /* of this routine by chking grant_lock_flag. */ /* DO NOT close the fd & reset the ld 'cause it will be */ /* re-used */ if (ld.l_xxx == GRANT_LOCK_FLAG) *grant_lock_flag = TRUE; else if (ld.l_xxx == LOCKD_LAST_REF) { remove_fd(fd); close(fd); } nlm_resp->lstat = nlm_granted; } return (nlm_resp); } remote_result * remote_unlock(a, choice) struct reclock *a; int choice; { if (debug) printf("enter remote_unlock\n"); if (choice == MSG) { if (nlm_call(NLM_UNLOCK_MSG, a, 0) == -1) a->rel = 1; /* rpc error, discard */ } else { printf("rpc not supported\n"); a->rel = 1; /* rpc error, discard */ } (void) remove_req_in_me(a); return (NULL); /* no reply available */ } remote_result * local_test(a) struct reclock *a; { int fd, cmd; struct eflock ld; int nofd = used_fd; /* Save the previous no of used */ /* fd, used to check if this */ /* file is opened or not */ /* * convert fh to fd */ if ((fd = get_fd(a)) < 0) { if (fd == -1) nlm_resp->lstat = nlm_denied_nolocks; else nlm_resp->lstat = nlm_denied; return (nlm_resp); } /* * test the lock */ cmd = F_RGETLK; if (a->exclusive) ld.l_type = F_WRLCK; else ld.l_type = F_RDLCK; ld.l_whence = 0; ld.l_start = (a->lck.lox.base >= 0) ? a->lck.lox.base : 0; ld.l_len = a->lck.lox.length; ld.l_pid = a->lck.lox.pid; ld.l_rpid = a->lck.lox.rpid; ld.l_rsys = a->lck.lox.rsys; ld.l_xxx = 0; if (fcntl(fd, cmd, &ld) == -1) { if (errno == EINTR) { nlm_resp->lstat = blocking; a->w_flag = 1; } else if (errno == ENOLCK) { perror("fcntl"); printf("rpc.lockd: out of lock.\n"); nlm_resp->lstat = nlm_denied_nolocks; } else { perror("fcntl"); printf("rpc.lockd: unable to test a lock. \n"); nlm_resp->lstat = nlm_denied; } } else { if (ld.l_type == F_UNLCK) { nlm_resp->lstat = nlm_granted; a->lck.lox.type = ld.l_type; } else { nlm_resp->lstat = nlm_denied; a->lck.lox.type = ld.l_type; a->lck.lox.base = ld.l_start; a->lck.lox.length = ld.l_len; a->lck.lox.pid = ld.l_pid; a->lck.lox.rpid = ld.l_rpid; a->lck.lox.rsys = ld.l_rsys; } } if (debug) { printf("ld.l_start=%d ld.l_len=%d ld.l_rpid=%d ld.l_rsys=%x\n", ld.l_start, ld.l_len, ld.l_rpid, ld.l_rsys); } /* * It is a new fd, so we close it. Or we leave it open. */ if (ld.l_xxx == LOCKD_LAST_REF) { remove_fd(fd); close(fd); } return (nlm_resp); } remote_result * remote_test(a, choice) struct reclock *a; int choice; { if (debug) printf("enter remote_test\n"); if (choice == MSG) { if (nlm_call(NLM_TEST_MSG, a, 0) == -1) a->rel = 1; } else { printf("rpc not supported\n"); a->rel = 1; } return (NULL); } remote_result * remote_cancel(a, choice) struct reclock *a; int choice; { msg_entry *msgp; if (debug) printf("enter remote_cancel(%x)\n", a); if (choice == MSG){ if (nlm_call(NLM_CANCEL_MSG, a, 0) == -1) a->rel = 1; } else { /* rpc case */ printf("rpc not supported\n"); a->rel = 1; } if ((msgp = retransmitted(a, KLM_LOCK)) != NULL) { /* msg is being processed */ if (debug) printf("remove msg (%x) due to remote cancel\n", msgp->req); /* don't free the reclock here as remove_req_in_me() will do */ /* that. */ msgp->req->rel = 0; dequeue(msgp); } remove_req_in_me(a); return (NULL); } remote_result * local_grant(a) struct reclock *a; { msg_entry *msgp; remote_result *resp; if (debug) printf("enter local_grant(%x)...\n", a); msgp = msg_q; while (msgp != NULL) { if (same_bound(&(msgp->req->alock.lox), &(a->alock.lox)) && same_type(&(msgp->req->alock.lox), &(a->alock.lox)) && (msgp->req->alock.lox.pid == a->alock.lox.pid)) { /* upgrade request from pending to granted in */ /* monitoring list for recovery */ (void) upgrade_req_in_me(msgp->req); /* * if the reply is for older request, set the * reply result so that the nxt poll by the kernel * will get this result */ if (msgp->reply != NULL) { msgp->reply->lstat = klm_granted; /* if no reply is set before, lets save this reply */ } else if ((resp = get_res()) != NULL) { resp->lstat = klm_granted; msgp->reply = resp; } break; } msgp = msgp->nxt; } nlm_resp->lstat = nlm_granted; return (nlm_resp); } remote_result * remote_grant(a, choice) struct reclock *a; int choice; { struct reclock *req; if (debug) printf("enter remote_grant...\n"); /* reply to the granted lock that was queued in our blocking lock */ /* list */ if ((req = find_block_req(a)) != NULL) { if (choice == MSG) { if (nlm_call(NLM_GRANTED_MSG, req, 0) != -1) dequeue_block_req(req); } else { printf("rpc not supported\n"); } } return (NULL); } remote_result * cont_lock(a, resp) struct reclock *a; remote_result *resp; { if (debug) { printf("enter cont_lock (%x) ID=%d \n", a, a->lck.lox.LockID); } switch (resp->lstat) { case nlm_granted: a->rel = 0; if (add_mon(a, 1) == -1) printf("rpc.lockd: add_mon failed in cont_lock.\n"); return (resp); case denied: case nolocks: a->rel = 1; a->block = FALSE; a->lck.lox.granted = 0; return (resp); case deadlck: a->rel = 1; a->block = TRUE; a->lck.lox.granted = 0; return (resp); case blocking: a->rel = 0; a->w_flag = 1; a->block = TRUE; return (resp); case grace: a->rel = 0; release_res(resp); return (NULL); default: a->rel = 1; release_res(resp); printf("unknown lock return: %d\n", resp->lstat); return (NULL); } } remote_result * cont_unlock(a, resp) struct reclock *a; remote_result *resp; { if (debug) printf("enter cont_unlock\n"); a->rel = 1; switch (resp->lstat) { case nlm_granted: return (resp); case denied: /* impossible */ case nolocks: return (resp); case blocking: /* impossible */ a->w_flag = 1; return (resp); case grace: a->rel = 0; release_res(resp); return (NULL); default: a->rel = 0; release_res(resp); fprintf(stderr, "rpc.lockd: unkown rpc_unlock return: %d\n", resp->lstat); return (NULL); } } remote_result * cont_test(a, resp) struct reclock *a; remote_result *resp; { if (debug) printf("enter cont_test\n"); a->rel = 1; switch (resp->lstat) { case grace: a->rel = 0; release_res(resp); return (NULL); case nlm_granted: case denied: if (debug) printf("lock blocked by %d, (%d, %d)\n", resp->lholder.svid, resp->lholder.l_offset, resp->lholder.l_len); return (resp); case nolocks: return (resp); case blocking: a->w_flag = 1; return (resp); default: fprintf(stderr, "rpc.lockd: cont_test: unknown return: %d\n", resp->lstat); release_res(resp); return (NULL); } } remote_result * cont_cancel(a, resp) struct reclock *a; remote_result *resp; { if (debug) printf("enter cont_cancel\n"); return(cont_unlock(a, resp)); } remote_result * cont_reclaim(a, resp) struct reclock *a; remote_result *resp; { remote_result *local; if (debug) printf("enter cont_reclaim\n"); switch (resp->lstat) { case nlm_granted: case denied: case nolocks: case blocking: local = resp; break; case grace: if (a->reclaim) fprintf(stderr, "rpc.lockd: reclaim lock req(%x) is returned due to grace period, impossible\n", a); local = NULL; break; default: printf("unknown cont_reclaim return: %d\n", resp->lstat); local = NULL; break; } if (local == NULL) release_res(resp); return (local); } remote_result * cont_grant(a, resp) struct reclock *a; remote_result *resp; { if (debug) printf("enter cont_grant...\n"); return (resp); } /* * Run through the list of file descriptors and close any that are no * longer in use. This is typically done after a client reboot. */ void check_fds() { struct fd_table *t; struct eflock ld; int i; if (debug) printf("Checking for files to close...\n"); for (t = fd_table, i = 0; i <= lastfd; t++, i++) { if (t->fd > 0) { ld.l_type = F_RDLCK; ld.l_whence = 0; ld.l_start = 0; ld.l_len = 0; ld.l_pid = getpid(); ld.l_rpid = getpid(); /* * Make the rsys be non-zero, to avoid triggering possible * kernel checks that lockd calls have non-zero rsys's. */ ld.l_rsys = 42; ld.l_xxx = 0; if (fcntl(t->fd, F_RGETLK, &ld) < 0) { perror("fcntl"); printf("rpc.lockd: couldn't check for unneeded files\n"); } else { if (ld.l_xxx == LOCKD_LAST_REF) { if (debug) printf("close(%d)\n", t->fd); close(t->fd); remove_fd(t->fd); } } } } if (debug) printf("...done checking for files to close.\n"); }