root/usr/src/cmd/svr4pkg/installf/main.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */


#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <pkginfo.h>
#include <pkgstrct.h>
#include <pkglocs.h>
#include <locale.h>
#include <libintl.h>
#include <instzones_api.h>
#include <pkglib.h>
#include <install.h>
#include <libadm.h>
#include <libinst.h>
#include "installf.h"

#define BASEDIR "/BASEDIR/"

#define INSTALF (*prog == 'i')
#define REMOVEF (*prog == 'r')

#define MSG_MANMOUNT    "Assuming mounts were provided."

#define ERR_PKGNAME_TOO_LONG    \
"The package name specified on the command line\n" \
"exceeds the maximum package name length: a package name may contain a\n" \
"maximum of <%d> characters; however, the package name specified on\n" \
"the command line contains <%d> characters, which exceeds the maximum\n" \
"package name length by <%d> characters. Please specify a package name\n" \
"that contains no more than <%d> characters."

#define ERR_DB_GET "unable to retrieve entries from the database."
#define ERR_DB_PUT "unable to update the package database."
#define ERR_ROOT_SET    "Could not set install root from the environment."
#define ERR_ROOT_CMD    "Command line install root contends with environment."
#define ERR_CLASSLONG   "classname argument too long"
#define ERR_CLASSCHAR   "bad character in classname"
#define ERR_INVAL       "package instance <%s> is invalid"
#define ERR_NOTINST     "package instance <%s> is not installed"
#define ERR_MERG        "unable to merge contents file"
#define ERR_SORT        "unable to sort contents file"
#define ERR_I_FAIL      "installf did not complete successfully"
#define ERR_R_FAIL      "removef did not complete successfully"
#define ERR_NOTROOT     "You must be \"root\" for %s to execute properly."
#define ERR_USAGE0      "usage:\n" \
        "\t%s [[-M|-A] -R host_path] [-V ...] pkginst path " \
        "[path ...]\n" \
        "\t%s [[-M|-A] -R host_path] [-V ...] pkginst path\n"

#define ERR_USAGE1      "usage:\n" \
        "\t%s [[-M] -R host_path] [-V ...] [-c class] <pkginst> " \
        "<path>\n" \
        "\t%s [[-M] -R host_path] [-V ...] [-c class] <pkginst> " \
        "<path> <specs>\n" \
        "\t   where <specs> may be defined as:\n" \
        "\t\tf <mode> <owner> <group>\n" \
        "\t\tv <mode> <owner> <group>\n" \
        "\t\te <mode> <owner> <group>\n" \
        "\t\td <mode> <owner> <group>\n" \
        "\t\tx <mode> <owner> <group>\n" \
        "\t\tp <mode> <owner> <group>\n" \
        "\t\tc <major> <minor> <mode> <owner> <group>\n" \
        "\t\tb <major> <minor> <mode> <owner> <group>\n" \
        "\t\ts <path>=<srcpath>\n" \
        "\t\tl <path>=<srcpath>\n" \
        "\t%s [[-M] -R host_path] [-V ...] [-c class] -f pkginst\n"

#define CMD_SORT        "sort +0 -1"

#define LINK    1

extern char     dbst;                   /* libinst/pkgdbmerg.c */

struct cfextra **extlist;
struct pinfo **eptlist;

char    *classname = NULL;
char    *pkginst;
char    *uniTmp;
char    *abi_sym_ptr;
char    *ulim;
char    *script;

int     nosetuid;
int     nocnflct;
int     warnflag = 0;

/* libadm/pkgparam.c */
extern void     set_PKGADM(char *newpath);
extern void     set_PKGLOC(char *newpath);

extern void set_limit(void);

int
main(int argc, char **argv)
{
        VFP_T           *cfTmpVfp;
        PKGserver       pkgserver = NULL;
        char            *tp;
        char            *prog;
        char            *pt;
        char            *vfstab_file = NULL;
        char            *temp_cl_basedir;
        char            outbuf[PATH_MAX];
        int             c;
        int             dbchg;
        int             err;
        int             fflag = 0;
        int             map_client = 1;
        int             n;
        int             pkgrmremote = 0;        /* don't remove remote files */
        struct cfent    *ept;

        /* hookup signals */

        (void) signal(SIGHUP, exit);
        (void) signal(SIGINT, exit);
        (void) signal(SIGQUIT, exit);

        /* initialize locale mechanism */

        (void) setlocale(LC_ALL, "");

#if     !defined(TEXT_DOMAIN)   /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST"
#endif  /* !defined(TEXT_DOMAIN) */

        (void) textdomain(TEXT_DOMAIN);

        /* determine program name */

        prog = set_prog_name(argv[0]);

        /* tell instzones interface how to access package output functions */

        z_set_output_functions(echo, echoDebug, progerr);

        /* only allow root to run this program */

        if (getuid() != 0) {
                progerr(gettext(ERR_NOTROOT), prog);
                exit(1);
        }

        ulim = getenv("PKG_ULIMIT");
        script = getenv("PKG_PROC_SCRIPT");

        if (ulim && script) {
                set_limit();
                clr_ulimit();
        }

        /* bug id 4244631, not ABI compliant */
        abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
        if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0)
                set_nonABI_symlinks();

        /* bugId 4012147 */
        if ((uniTmp = getenv("PKG_NO_UNIFIED")) != NULL)
                map_client = 0;
        if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
                progerr(gettext(ERR_ROOT_SET));
                exit(1);
        }

        while ((c = getopt(argc, argv, "c:V:fAMR:?")) != EOF) {
                switch (c) {
                        case 'f':
                        fflag++;
                        break;

                        case 'c':
                        classname = optarg;
                        /* validate that classname is acceptable */
                        if (strlen(classname) > (size_t)CLSSIZ) {
                                progerr(gettext(ERR_CLASSLONG));
                                exit(1);
                        }
                        for (pt = classname; *pt; pt++) {
                                if (!isalpha(*pt) && !isdigit(*pt)) {
                                        progerr(gettext(ERR_CLASSCHAR));
                                        exit(1);
                                }
                        }
                        break;

                /*
                 * Don't map the client filesystem onto the server's. Assume
                 * the mounts have been made for us.
                 */
                        case 'M':
                        map_client = 0;
                        break;

                /*
                 * Allow admin to establish the client filesystem using a
                 * vfstab-like file of stable format.
                 */
                        case 'V':
                        vfstab_file = flex_device(optarg, 2);
                        map_client = 1;
                        break;

                        case 'A':
                        pkgrmremote++;
                        break;

                        case 'R':       /* added for newroot option */
                        if (!set_inst_root(optarg)) {
                                progerr(gettext(ERR_ROOT_CMD));
                                exit(1);
                        }
                        break;

                        default:
                        usage();
                        /*NOTREACHED*/
                        /*
                         * Although usage() calls a noreturn function,
                         * needed to add return (1);  so that main() would
                         * pass compilation checks. The statement below
                         * should never be executed.
                         */
                        return (1);
                }
        }

        if (pkgrmremote && (!is_an_inst_root() || fflag || INSTALF)) {
                usage();
                /*NOTREACHED*/
        }

        /*
         * Get the mount table info and store internally.
         */
        if (get_mntinfo(map_client, vfstab_file))
                exit(1);

        /*
         * This function defines the standard /var/... directories used later
         * to construct the paths to the various databases.
         */
        (void) set_PKGpaths(get_inst_root());

        /*
         * If this is being installed on a client whose /var filesystem is
         * mounted in some odd way, remap the administrative paths to the
         * real filesystem. This could be avoided by simply mounting up the
         * client now; but we aren't yet to the point in the process where
         * modification of the filesystem is permitted.
         */
        if (is_an_inst_root()) {
                int fsys_value;

                fsys_value = fsys(get_PKGLOC());
                if (use_srvr_map_n(fsys_value))
                        set_PKGLOC(server_map(get_PKGLOC(), fsys_value));

                fsys_value = fsys(get_PKGADM());
                if (use_srvr_map_n(fsys_value))
                        set_PKGADM(server_map(get_PKGADM(), fsys_value));
        }

        /*
         * get the package name and verify length is not too long
         */

        pkginst = argv[optind++];
        if (pkginst == NULL) {
                usage();
                /*NOTREACHED*/

        }

        n = strlen(pkginst);
        if (n > PKGSIZ) {
                progerr(gettext(ERR_PKGNAME_TOO_LONG), PKGSIZ, n, n-PKGSIZ,
                    PKGSIZ);
                usage();
                /*NOTREACHED*/
        }

        /*
         * The following is used to setup the environment. Note that the
         * variable 'BASEDIR' is only meaningful for this utility if there
         * is an install root, recorded in PKG_INSTALL_ROOT. Otherwise, this
         * utility can create a file or directory anywhere unfettered by
         * the basedir associated with the package instance.
         */
        if ((err = set_basedirs(0, NULL, pkginst, 1)) != 0)
                exit(err);

        if (INSTALF)
                mkbasedir(0, get_basedir());

        if (fflag) {
                /* installf and removef must only have pkginst */
                if (optind != argc) {
                        usage();
                        /*NOTREACHED*/
                }
        } else {
                /*
                 * installf and removef must have at minimum
                 * pkginst & pathname specified on command line
                 */
                if (optind >= argc) {
                        usage();
                        /*NOTREACHED*/
                }
        }
        if (REMOVEF) {
                if (classname) {
                        usage();
                }
        }
        if (pkgnmchk(pkginst, "all", 0)) {
                progerr(gettext(ERR_INVAL), pkginst);
                exit(1);
        }
        if (fpkginst(pkginst, NULL, NULL) == NULL) {
                progerr(gettext(ERR_NOTINST), pkginst);
                exit(1);
        }

        /*
         * This maps the client filesystems into the server's space.
         */
        if (map_client && !mount_client())
                logerr(gettext(MSG_MANMOUNT));

        /* open the package database (contents) file */

        if (!ocfile(&pkgserver, &cfTmpVfp, 0L)) {
                quit(1);
        }

        if (fflag) {
                dbchg = dofinal(pkgserver, cfTmpVfp, REMOVEF, classname, prog);
        } else {
                if (INSTALF) {
                        dbst = INST_RDY;
                        if (installf(argc-optind, &argv[optind]))
                                quit(1);
                } else {
                        dbst = RM_RDY;
                        removef(argc-optind, &argv[optind]);
                }

                dbchg = pkgdbmerg(pkgserver, cfTmpVfp, extlist);
                if (dbchg < 0) {
                        progerr(gettext(ERR_MERG));
                        quit(99);
                }
        }

        if (dbchg) {
                if ((n = swapcfile(pkgserver, &cfTmpVfp, pkginst, 1))
                    == RESULT_WRN) {
                        warnflag++;
                } else if (n == RESULT_ERR) {
                        quit(99);
                }
        }

        relslock();

        if (REMOVEF && !fflag) {
                for (n = 0; extlist[n]; n++) {
                        ept = &(extlist[n]->cf_ent);

                        /* Skip duplicated paths */
                        if ((n > 0) && (strncmp(ept->path,
                            extlist[n-1]->cf_ent.path, PATH_MAX) == 0)) {
                                continue;
                        }

                        if (!extlist[n]->mstat.shared) {
                                /*
                                 * Only output paths that can be deleted.
                                 * so need to skip if the object is owned
                                 * by a remote server and removal is not
                                 * being forced.
                                 */
                                if (ept->pinfo &&
                                    (ept->pinfo->status == SERVED_FILE) &&
                                    !pkgrmremote)
                                        continue;

                                c = 0;
                                if (is_a_cl_basedir() && !is_an_inst_root()) {
                                        /*
                                         * A path in contents db might have
                                         * other prefix than BASEDIR of the
                                         * package
                                         */
                                        temp_cl_basedir = get_client_basedir();
                                        if (strncmp(ept->path, temp_cl_basedir,
                                            strlen(temp_cl_basedir)) == 0) {
                                                c = strlen(temp_cl_basedir);
                                                (void) snprintf(outbuf,
                                                    sizeof (outbuf), "%s/%s\n",
                                                    get_basedir(),
                                                    &(ept->path[c]));
                                        } else {
                                                (void) snprintf(outbuf,
                                                    sizeof (outbuf),
                                                    "%s\n", &(ept->path[c]));
                                        }
                                } else if (is_an_inst_root()) {
                                        (void) snprintf(outbuf, sizeof (outbuf),
                                            "%s/%s\n", get_inst_root(),
                                            &(ept->path[c]));
                                } else {
                                        (void) snprintf(outbuf, sizeof (outbuf),
                                            "%s\n", &(ept->path[c]));
                                }
                                canonize(outbuf);
                                (void) printf("%s", outbuf);
                        }
                }
        } else if (INSTALF && !fflag) {
                for (n = 0; extlist[n]; n++) {
                        ept = &(extlist[n]->cf_ent);

                        if (strchr("dxcbp", ept->ftype)) {
                                tp = fixpath(ept->path);
                                (void) averify(1, &ept->ftype, tp, &ept->ainfo);
                        }
                }
        }

        pkgcloseserver(pkgserver);

        z_destroyMountTable();

        quit(warnflag ? 1 : 0);
        /* LINTED: no return */
}

void
quit(int n)
{
        char *prog = get_prog_name();

        unmount_client();

        if (ulim && script) {
                if (REMOVEF) {
                        set_ulimit(script, gettext(ERR_R_FAIL));
                } else {
                        set_ulimit(script, gettext(ERR_I_FAIL));
                }
        }

        exit(n);
}

void
usage(void)
{
        char *prog = get_prog_name();

        if (REMOVEF) {
                (void) fprintf(stderr, gettext(ERR_USAGE0), prog, prog);
        } else {
                (void) fprintf(stderr, gettext(ERR_USAGE1), prog, prog, prog);
        }
        exit(1);
}