From owner-freebsd-standards Thu Mar 21 16:10:44 2002 Delivered-To: freebsd-standards@freebsd.org Received: from quic.net (romulus.quic.net [216.23.27.8]) by hub.freebsd.org (Postfix) with SMTP id 5003F37B41A for ; Thu, 21 Mar 2002 16:10:29 -0800 (PST) Received: (qmail 22796 invoked by uid 1032); 22 Mar 2002 00:10:31 -0000 From: utsl@quic.net Date: Thu, 21 Mar 2002 19:10:31 -0500 To: freebsd-standards@freebsd.org Subject: utmpx implementation Message-ID: <20020322001031.GA21735@quic.net> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="Dxnq1zWXvFF0Q93v" Content-Disposition: inline User-Agent: Mutt/1.3.27i Sender: owner-freebsd-standards@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.ORG --Dxnq1zWXvFF0Q93v Content-Type: text/plain; charset=us-ascii Content-Disposition: inline I have written a utmpx implementation. It is written from scratch from the SUSv2 spec (also in POSIX, I believe), and is under BSD license. I have also examined other systems, and included what made sense to me. I have partially implemented reentrant functions, but haven't finished yet. Suggestions and patches are welcome. --Dxnq1zWXvFF0Q93v Content-Type: text/x-csrc; charset=us-ascii Content-Disposition: attachment; filename="utmpx.c" /* * Copyright (c) 2002 * Nathan Hawkins. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include #include #include #include "utmpx.h" static int utx_fd=-1; static off_t utx_offset=-1; static struct utmpx utx_last; static char default_utmpxfile[]=_PATH_UTMPX; static char *utmpxfile=&default_utmpxfile[0]; static int utx_ro=0; static int openutxfile (void) { if(utx_fd == -1) { utx_fd = open(utmpxfile, O_RDWR); if(utx_fd == -1) { utx_fd = open(utmpxfile, O_RDONLY); utx_ro = 1; /* set read-only flag */ if(utx_fd == -1) return (1); } else utx_ro=0; /* clear read-only flag */ } utx_offset = 0; return (0); } /* * setutxent rewinds the utmpx file. If the file isn't open, it does nothing. */ void setutxent (void) { if (utx_fd == -1) return; utx_offset = lseek(utx_fd, 0, SEEK_SET); } /* * endutxent closes the utmpx file, and restores the filename to the default. */ void endutxent (void) { if (utx_fd != -1) { close (utx_fd); utx_fd = -1; } utmpxfile = default_utmpxfile; } /* * utmpxname is a nonstandard function used to select an alternate utmpx file. * The string passed here isn't used until one of the other functions opens the * file. endutxent resets the filename to default. */ void utmpxname (const char *utmpx) { if (utmpx != NULL) utmpxfile = (char *)utmpx; } static struct utmpx * getutxent_internal (struct utmpx *ut) { /* either read a full struct utmpx from the file or return NULL */ if (read(utx_fd, ut, sizeof (struct utmpx)) != sizeof (struct utmpx)) return (NULL); utx_offset += sizeof (struct utmpx); return (ut); } struct utmpx * getutxent_r (struct utmpx *buffer) { struct utmpx *ut; int flag; /* open the file if it isn't already */ if (utx_fd == -1 && openutxfile()) return (NULL); /* lock the file for reading */ flag=flock(utx_fd,LOCK_SH); if (flag == -1) return (NULL); /* failed to get the lock */ ut=getutxent_internal(buffer); flag=flock(utx_fd,LOCK_UN); if (flag == -1) return (NULL); /* failed to unlock for some reason */ return (ut); } /* * According to SUSv2: * * The getutxid() function searches forward from the current point in the * database. If the ut_type value of the utmpx structure pointed to by id is * BOOT_TIME, OLD_TIME or NEW_TIME, then it stops when it finds an entry with a * matching ut_type value. If the ut_type value is INIT_PROCESS, LOGIN_PROCESS, * USER_PROCESS, or DEAD_PROCESS, then it stops when it finds an entry whose * type is one of these four and whose ut_id member matches the ut_id member of * the utmpx structure pointed to by id. If the end of the database is reached * without a match, getutxid() fails. */ struct utmpx * getutxid_r (const struct utmpx *ut, struct utmpx *buffer) { int finished; struct utmpx *last; int flag; /* open the file if it isn't already */ if (utx_fd == -1 && openutxfile()) return (NULL); /* lock the file for reading */ flag=flock(utx_fd,LOCK_SH); if (flag == -1) return (NULL); /* failed to get the lock */ for (;;) { if ((last=getutxent_internal(buffer)) == NULL) break; switch (ut->ut_type) { case BOOT_TIME: case OLD_TIME: case NEW_TIME: if (ut->ut_type == last->ut_type) finished = 1; break; case INIT_PROCESS: case LOGIN_PROCESS: case USER_PROCESS: case DEAD_PROCESS: if (ut->ut_type == last->ut_type && strncmp(ut->ut_id,last->ut_id,4) == 0) finished = 1; break; /* evidently this function ignores other types */ default: break; } if (finished) break; } flag=flock(utx_fd,LOCK_UN); if (flag == -1) return (NULL); /* failed to unlock for some reason */ return (last); } /* * According to SUSv2: * * The getutxline() function searches forward from the current point in the * database until it finds an entry of the type LOGIN_PROCESS or USER_PROCESS * which also has a ut_line value matching that in the utmpx structure pointed * to by line. If the end of the database is reached without a match, * getutxline() fails. */ struct utmpx * getutxline_r (const struct utmpx *ut, struct utmpx *buffer) { int finished; struct utmpx *last; int flag; /* open the file if it isn't already */ if (utx_fd == -1 && openutxfile()) return (NULL); /* lock the file for reading */ flag=flock(utx_fd,LOCK_SH); if (flag == -1) return (NULL); /* failed to get the lock */ for (;;) { if ((last=getutxent_internal(buffer)) == NULL) break; switch (last->ut_type) { case LOGIN_PROCESS: case USER_PROCESS: if (strncmp(ut->ut_line,last->ut_line,UT_LINESIZE) == 0) finished = 1; break; /* evidently this function ignores other types */ default: break; } if (finished) break; } flag=flock(utx_fd,LOCK_UN); if (flag == -1) return (NULL); /* failed to unlock for some reason */ return (last); } /* * According to SUSv2: * * If the process has appropriate privileges, the pututxline() function writes * out the structure into the user accounting database. It uses getutxid() to * search for a record that satisfies the request. If this search succeeds, * then the entry is replaced. Otherwise, a new entry is made at the end of the * user accounting database. * * The implicit read done by pututxline() (if it finds that it is not already * at the correct place in the user accounting database) will not modify the * static structure returned by getutxent(), getutxid() or getutxline(), if the * application has just modified this structure and passed the pointer back to * pututxline(). */ struct utmpx * pututxline (const struct utmpx *ut) { struct utmpx findbuf; struct utmpx *found; int flag; if (utx_ro) return (NULL); /* don't have write access to utmpx */ if ((found=getutxid_r(ut,&findbuf))!=NULL) { /* try to rewind the file by exactly one record */ utx_offset = lseek(utx_fd, utx_offset - sizeof (struct utmpx), SEEK_CUR); } else { /* seek to the end of the file */ utx_offset = lseek(utx_fd, 0, SEEK_END); } /* lock the file for writing */ flag=flock(utx_fd,LOCK_EX); if (flag == -1) return (NULL); /* failed to get the lock */ if (write(utx_fd, ut, sizeof (struct utmpx)) != sizeof (struct utmpx)) { /* * In case of failed write to the end of the file, truncate the * record. This keeps the file format from getting corrupted. */ if (found != NULL) { ftruncate(utx_fd, utx_offset); return (NULL); } else { return (NULL); } } utx_offset += sizeof (struct utmpx); flag=flock(utx_fd,LOCK_UN); if (flag == -1) return (NULL); /* failed to unlock for some reason */ return ((struct utmpx *)ut); } struct utmpx * getutxent(void) { getutxent_r(&utx_last); } struct utmpx * getutxid (const struct utmpx *ut) { getutxid_r(ut,&utx_last); } struct utmpx * getutxline (const struct utmpx *ut) { getutxid_r(ut,&utx_last); } --Dxnq1zWXvFF0Q93v Content-Type: text/x-chdr; charset=us-ascii Content-Disposition: attachment; filename="utmpx.h" /* * Copyright (c) 2002 * Nathan Hawkins. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef _UTMPX_H_ #define _UTMPX_H_ #include #include #define _PATH_UTMPX "/var/run/utmpx" #define UTMPX_FILE _PATH_UTMPX /* features in utmpx */ #define _HAVE_UT_PID 1 #define _HAVE_UT_ID 1 #define _HAVE_UT_TV 1 #define _HAVE_UT_TYPE 1 #define _HAVE_UT_HOST 1 /* size of character arrays in struct utmpx */ #define UT_LINESIZE 32 #define UT_NAMESIZE 32 #define UT_HOSTSIZE 256 struct exit_status { short int e_termination; /* Process termination status. */ short int e_exit; /* Process exit status. */ }; struct utmpx { pid_t ut_pid; char ut_id[4]; /* reserve space for IPv6 addresses */ u_int32_t ut_addr6[4]; long int ut_session; /* used by X? */ struct timeval ut_tv; struct exit_status ut_exit; short int ut_type; char ut_line[UT_LINESIZE]; char ut_user[UT_NAMESIZE]; char ut_host[UT_HOSTSIZE]; char _unused[20]; }; /* values for ut_type */ #define EMPTY 0 /* Empty struct */ #define RUN_LVL 1 /* SysV runlevel */ #define BOOT_TIME 2 /* System boot */ #define NEW_TIME 3 /* Mark new time at time change */ #define OLD_TIME 4 /* Mark old time at time change */ #define INIT_PROCESS 5 /* Process started by init */ #define LOGIN_PROCESS 6 /* User login */ #define USER_PROCESS 7 /* Normal process. */ #define DEAD_PROCESS 8 /* Terminated process. */ #define ACCOUNTING 9 __BEGIN_DECLS void endutxent(void); void setutxent(void); struct utmpx *getutxent(void); struct utmpx *getutxid(const struct utmpx *); struct utmpx *getutxline(const struct utmpx *); struct utmpx *pututxline(const struct utmpx *); void utmpxname(const char *); __END_DECLS #endif /* !_UTMPX_H_ */ --Dxnq1zWXvFF0Q93v-- To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-standards" in the body of the message