#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <string.h>
#include <strings.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/statvfs.h>
#include <signal.h>
#include <limits.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <pkglocs.h>
#include <locale.h>
#include <libintl.h>
#include <pkglib.h>
#include "libinst.h"
#include "libadm.h"
#define LOCKFILE ".pkg.lock.client"
#define LOCKFILESERV ".pkg.lock"
#define LOCKWAIT 10
#define LOCKRETRY 20
#define ERR_COMMIT "WARNING: unable to commit contents database update"
#define ERR_NOCLOSE "WARNING: unable to close <%s>"
#define ERR_NOUNLINK_LATENT "WARNING: unable to unlink latent <%s>"
#define ERR_LINK_FAIL "link(%s, %s) failed (errno %d)"
#define ERR_NORENAME_CONTENTS "unable to establish contents file <%s> "\
"from <%s>"
#define ERR_RENAME_FAIL "rename(%s, %s) failed (errno %d)"
#define ERR_RESTORE_FAIL "attempt to restore <%s> failed"
#define ERR_NOUNLINK "WARNING: unable to unlink <%s>"
#define ERR_FCLOSE_FAIL "fclose failed (errno %d)"
#define ERR_ERRNO "(errno %d: %s)"
#define ERR_NOTMPOPEN "unable to open temporary contents file image"
#define ERR_CFBACK "Not enough space to backup <%s>"
#define ERR_CREAT_CONT "unable to create contents file <%s>: %s"
#define ERR_ACCESS_CONT "unable to access contents file <%s>: %s"
#define ERR_CFBACK1 "Need=%llu blocks, Available=%llu blocks " \
"(block size=%d)"
#define ERR_NOCFILE "unable to locate contents file <%s>"
#define ERR_NOROPEN "unable to open <%s> for reading"
#define ERR_NOOPEN "unable to open <%s> for writing"
#define ERR_NOSTAT "unable to stat contents file <%s>"
#define ERR_NOSTATV "statvfs(%s) failed"
#define ERR_NOUPD "unable to update contents file"
#define ERR_DRCONTCP "unable to copy contents file to <%s>"
#define MSG_XWTING "NOTE: Waiting for exclusive access to the package " \
"database."
#define MSG_NOLOCK "NOTE: Couldn't lock the package database."
#define ERR_NOLOCK "Database lock failed."
#define ERR_OPLOCK "unable to open lock file <%s>."
#define ERR_MKLOCK "unable to create lock file <%s>."
#define ERR_LCKREM "unable to lock package database - remote host " \
"unavailable."
#define ERR_BADLCK "unable to lock package database - unknown error."
#define ERR_DEADLCK "unable to lock package database - deadlock condition."
#define ERR_TMOUT "unable to lock package database - too many retries."
#define ERR_CFDIR "unable to locate contents file directory"
static int active_lock;
static int lock_fd;
static char *pkgadm_dir;
int pkgWlock(int verbose);
static int pkgWunlock(void);
int relslock(void);
static void
do_alarm(int n)
{
(void) signal(SIGALRM, SIG_IGN);
(void) signal(SIGALRM, do_alarm);
(void) alarm(LOCKWAIT);
}
int
set_cfdir(char *cfdir)
{
char realcf[PATH_MAX];
char tmpcf[PATH_MAX];
int status;
if (cfdir == NULL) {
pkgadm_dir = get_PKGADM();
return (0);
}
if ((pkgadm_dir = strdup(cfdir)) == NULL) {
return (99);
}
(void) snprintf(tmpcf, sizeof (tmpcf), "%s/contents", pkgadm_dir);
if (access(tmpcf, F_OK) == 0) {
return (0);
}
(void) snprintf(realcf, sizeof (realcf), "%s/contents", get_PKGADM());
(void) pkgsync(NULL, get_PKGADM(), B_FALSE);
if (access(realcf, F_OK) != 0) {
int n;
n = open(tmpcf, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
if (n < 0) {
progerr(gettext(ERR_CREAT_CONT), tmpcf,
strerror(errno));
return (99);
}
(void) close(n);
} else {
status = copyf(realcf, tmpcf, (time_t)0);
if (status != 0) {
progerr(gettext(ERR_DRCONTCP), tmpcf);
return (99);
}
}
return (0);
}
int
ocfile(PKGserver *server, VFP_T **r_tmpvfp, fsblkcnt_t map_blks)
{
struct stat64 statb, statl;
struct statvfs64 svfsb;
fsblkcnt_t free_blocks;
fsblkcnt_t need_blocks;
fsblkcnt_t log_blocks;
VFP_T *tmpvfp = (VFP_T *)NULL;
char contents[PATH_MAX];
char logfile[PATH_MAX];
off_t cdiff_alloc;
PKGserver newserver;
if (pkgadm_dir == NULL) {
if (set_cfdir(NULL) != 0) {
progerr(gettext(ERR_CFDIR));
return (0);
}
}
if (!pkgWlock(1)) {
progerr(gettext(ERR_NOLOCK));
return (0);
}
if (*server != NULL) {
vfpTruncate(*r_tmpvfp);
(void) vfpClearModified(*r_tmpvfp);
return (1);
}
newserver = pkgopenserver(NULL, pkgadm_dir, B_FALSE);
if (newserver == NULL)
return (0);
(*r_tmpvfp) = (VFP_T *)NULL;
(void) snprintf(contents, sizeof (contents), "%s/contents", pkgadm_dir);
if (stat64(contents, &statb) == -1) {
int lerrno = errno;
progerr(gettext(ERR_NOCFILE), contents);
logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
pkgcloseserver(newserver);
return (0);
}
if (statvfs64(contents, &svfsb) == -1) {
int lerrno = errno;
progerr(gettext(ERR_NOSTATV), contents);
logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
pkgcloseserver(newserver);
return (0);
}
free_blocks = (((fsblkcnt_t)svfsb.f_frsize > 0) ?
howmany(svfsb.f_frsize, DEV_BSIZE) :
howmany(svfsb.f_bsize, DEV_BSIZE)) * svfsb.f_bfree;
(void) snprintf(logfile, sizeof (logfile), "%s/" PKGLOG, pkgadm_dir);
if (stat64(logfile, &statl) == -1)
log_blocks = 0;
else
log_blocks = nblk(statl.st_size, svfsb.f_bsize, svfsb.f_frsize);
need_blocks = map_blks * 3 +
log_blocks +
nblk(statb.st_size, svfsb.f_bsize, svfsb.f_frsize);
if ((need_blocks + 10) > free_blocks) {
progerr(gettext(ERR_CFBACK), contents);
progerr(gettext(ERR_CFBACK1), need_blocks, free_blocks,
DEV_BSIZE);
pkgcloseserver(newserver);
return (0);
}
if (vfpOpen(&tmpvfp, (char *)NULL, "w", VFP_NONE) != 0) {
int lerrno = errno;
progerr(gettext(ERR_NOTMPOPEN));
logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
pkgcloseserver(newserver);
return (0);
}
cdiff_alloc = map_blks * DEV_BSIZE;
cdiff_alloc += cdiff_alloc/2;
if (cdiff_alloc < 1000000)
cdiff_alloc += 1000000;
if (vfpSetSize(tmpvfp, cdiff_alloc) != 0) {
int lerrno = errno;
progerr(gettext(ERR_NOTMPOPEN));
logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
(void) vfpClose(&tmpvfp);
pkgcloseserver(newserver);
return (0);
}
(*r_tmpvfp) = tmpvfp;
*server = newserver;
return (1);
}
int
socfile(PKGserver *server, boolean_t quiet)
{
boolean_t readonly = B_FALSE;
PKGserver newserver;
if (pkgadm_dir == NULL) {
if (set_cfdir(NULL) != 0) {
progerr(gettext(ERR_CFDIR));
return (0);
}
}
if (!pkgWlock(0)) {
if (!quiet)
logerr(gettext(MSG_NOLOCK));
readonly = B_TRUE;
}
newserver = pkgopenserver(NULL, pkgadm_dir, readonly);
if (newserver == NULL)
return (0);
*server = newserver;
return (1);
}
int
swapcfile(PKGserver server, VFP_T **a_cfTmpVfp, char *pkginst, int dbchg)
{
char *pe;
char *pl;
char *ps;
char line[256];
char timeb[BUFSIZ];
int retval = RESULT_OK;
struct tm *timep;
time_t clock;
if (pkginst == (char *)NULL) {
dbchg = 0;
pkginst = "<unknown>";
}
if ((dbchg == 0) && (vfpGetModified(*a_cfTmpVfp) == 0)) {
(void) pkgWunlock();
return (retval);
}
pe = vfpGetCurrCharPtr(*a_cfTmpVfp);
ps = vfpGetFirstCharPtr(*a_cfTmpVfp);
pl = pe;
while ((pe > ps) && ((*pe == '\n') || (*pe == '\0'))) {
pe--;
}
while (pe > ps) {
if (*pe != '\n') {
pl = pe--;
} else if (*pl != '#') {
break;
} else {
*pl = '\0';
vfpSetLastCharPtr(*a_cfTmpVfp, pl);
pe--;
}
}
(void) time(&clock);
timep = localtime(&clock);
(void) strftime(timeb, sizeof (timeb), "%c\n", timep);
(void) snprintf(line, sizeof (line),
gettext("# Last modified by %s for %s package\n# %s"),
get_prog_name(), pkginst, timeb);
vfpPuts(*a_cfTmpVfp, line);
if (pkgservercommitfile(*a_cfTmpVfp, server) != 0) {
logerr(gettext(ERR_COMMIT));
vfpClose(a_cfTmpVfp);
pkgcloseserver(server);
(void) pkgWunlock();
return (RESULT_ERR);
}
return (relslock() == 0 ? RESULT_ERR : retval);
}
int
relslock(void)
{
if (!pkgWunlock()) {
int lerrno = errno;
progerr(gettext(ERR_NOUPD));
logerr(gettext(ERR_FCLOSE_FAIL), lerrno);
return (0);
}
return (1);
}
int
pkgWlock(int verbose)
{
int retry_cnt, retval;
char lockpath[PATH_MAX];
active_lock = 0;
(void) snprintf(lockpath, sizeof (lockpath),
"%s/%s", pkgadm_dir, LOCKFILE);
retry_cnt = LOCKRETRY;
if (access(lockpath, F_OK) == -1) {
lock_fd = open(lockpath, O_RDWR|O_CREAT|O_TRUNC|O_EXCL, 0644);
if (lock_fd < 0) {
if (verbose)
progerr(gettext(ERR_MKLOCK), lockpath);
return (0);
} else {
(void) fchmod(lock_fd, 0644);
}
} else {
if ((lock_fd = open(lockpath, O_RDWR)) == -1) {
if (verbose)
progerr(gettext(ERR_OPLOCK), lockpath);
return (0);
}
}
(void) signal(SIGALRM, do_alarm);
(void) alarm(LOCKWAIT);
retval = 0;
do {
if (lockf(lock_fd, F_LOCK, 0)) {
if (errno == EAGAIN || errno == EINTR)
logerr(gettext(MSG_XWTING));
else if (errno == ECOMM) {
logerr(gettext(ERR_LCKREM));
break;
} else if (errno == EBADF) {
logerr(gettext(ERR_BADLCK));
break;
} else if (errno == EDEADLK) {
logerr(gettext(ERR_DEADLCK));
break;
}
} else {
active_lock = 1;
retval = 1;
break;
}
} while (retry_cnt--);
(void) signal(SIGALRM, SIG_IGN);
if (retval == 0) {
if (retry_cnt == -1) {
logerr(gettext(ERR_TMOUT));
}
(void) pkgWunlock();
}
return (retval);
}
static int
pkgWunlock(void)
{
if (active_lock) {
active_lock = 0;
if (close(lock_fd))
return (0);
else
return (1);
} else
return (1);
}
int
iscfile(void)
{
char contents[PATH_MAX];
(void) snprintf(contents, PATH_MAX, "%s/contents", get_PKGADM());
return (access(contents, F_OK) == 0 ? 1 : 0);
}
int
vcfile(void)
{
int lerrno;
int fd;
char contents[PATH_MAX];
(void) snprintf(contents, sizeof (contents),
"%s/contents", get_PKGADM());
fd = open(contents, O_WRONLY | O_CREAT | O_EXCL, 0644);
if (fd >= 0) {
echo(gettext("## Software contents file initialized"));
(void) close(fd);
return (1);
}
lerrno = errno;
if (lerrno == EEXIST) {
return (1);
}
if (lerrno == EACCES) {
if (access(contents, F_OK) == 0) {
return (1);
}
if (errno == EACCES) {
progerr(gettext(ERR_ACCESS_CONT), contents,
strerror(lerrno));
logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
return (0);
}
}
progerr(gettext(ERR_CREAT_CONT), contents, strerror(lerrno));
logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
return (0);
}