blob: b4ab7846ab15df344f766eb2590bcb023bea02fd [file] [log] [blame] [raw]
/* Common utmpx implementation
* Copyright 2015-2017 Rivoreo
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include "utmpx.h"
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
static int utmpx_fd = -1;
//static const char *utmpx_file = UTMPX_FILE;
static char utmpx_file[PATH_MAX+1] = UTMPX_FILE;
static int open_utmpx() {
if(utmpx_fd != -1) return utmpx_fd;
utmpx_fd = open(utmpx_file, O_RDWR);
return utmpx_fd;
}
void setutxent() {
if(utmpx_fd == -1) open_utmpx();
else lseek(utmpx_fd, 0, SEEK_SET);
}
void endutxent() {
if(utmpx_fd == -1) return;
close(utmpx_fd);
utmpx_fd = -1;
}
struct utmpx *getutxent() {
static struct utmpx buffer;
if(utmpx_fd == -1 && open_utmpx() == -1) return NULL;
int s = read(utmpx_fd, &buffer, sizeof buffer);
if(s < sizeof buffer) return NULL;
return &buffer;
}
struct utmpx *getutxid(const struct utmpx *id) {
static struct utmpx buffer;
if(utmpx_fd == -1 && open_utmpx() == -1) return NULL;
int s;
while((s = read(utmpx_fd, &buffer, sizeof buffer))) {
if(s == -1 && (errno == EINTR || errno == EAGAIN)) continue;
if(s < sizeof buffer) break;
switch(id->ut_type) {
case RUN_LVL:
case BOOT_TIME:
case NEW_TIME:
case OLD_TIME:
if(id->ut_type == buffer.ut_type) return &buffer;
break;
case INIT_PROCESS:
case LOGIN_PROCESS:
case USER_PROCESS:
case DEAD_PROCESS:
switch(buffer.ut_type) {
case INIT_PROCESS:
case LOGIN_PROCESS:
case USER_PROCESS:
case DEAD_PROCESS:
if(memcmp(id->ut_id, buffer.ut_id, sizeof buffer.ut_id) == 0) return &buffer;
break;
}
break;
default:
return NULL;
}
}
return NULL;
}
struct utmpx *getutxline(const struct utmpx *line) {
static struct utmpx buffer;
if(utmpx_fd == -1 && open_utmpx() == -1) return NULL;
int s;
while((s = read(utmpx_fd, &buffer, sizeof buffer))) {
if(s == -1 && (errno == EINTR || errno == EAGAIN)) continue;
if(s < sizeof buffer) break;
switch(buffer.ut_type) {
case LOGIN_PROCESS:
case USER_PROCESS:
if(strncmp(line->ut_line, buffer.ut_line, sizeof buffer.ut_line) == 0) return &buffer;
break;
}
}
return NULL;
}
static int lock_write_end(int fd, const void *buffer, size_t count) {
int r;
if(lockf(fd, F_LOCK, 0) < 0) return -1;
if(lseek(fd, 0, SEEK_END) == (off_t)-1) {
r = -1;
goto end;
}
do {
r = write(fd, buffer, count);
} while(r == -1 && (errno == EINTR || errno == EAGAIN));
end:
lockf(fd, F_ULOCK, 0);
return r;
}
struct utmpx *pututxline(const struct utmpx *utx) {
static struct utmpx buffer;
if(!utx) {
errno = EFAULT;
return NULL;
}
//if(utmpx_fd == -1 && open_utmpx() == -1) return NULL;
setutxent();
if(utmpx_fd == -1) return NULL;
memcpy(&buffer, utx, sizeof buffer);
struct utmpx *old = getutxid(utx);
if(old) {
lseek(utmpx_fd, -(off_t)sizeof buffer, SEEK_CUR);
int s;
do {
s = write(utmpx_fd, &buffer, sizeof buffer);
} while(s == -1 && (errno == EINTR || errno == EAGAIN));
if(s < 0) return NULL;
} else if(lock_write_end(utmpx_fd, &buffer, sizeof buffer) < 0) {
return NULL;
}
return &buffer;
}
int utmpxname(const char *name) {
if(!name) {
errno = EFAULT;
return -1;
}
/*
if(!*name) {
errno = ENOENT;
return -1;
}*/
size_t len = strlen(name) + 1;
if(len > sizeof utmpx_file) {
errno = ENAMETOOLONG;
return -1;
}
memcpy(utmpx_file, name, len);
return 0;
}