/*
 *   Copyright (c) 1998 David Parsons. 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.
 *  3. All advertising materials mentioning features or use of this
 *     software must display the following acknowledgement:
 *     
 *   This product includes software developed by David Parsons
 *   (orc@pell.chi.il.us)
 *
 *  4. My name may not be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *     
 *  THIS SOFTWARE IS PROVIDED BY DAVID PARSONS ``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 DAVID
 *  PARSONS 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.
 */

static const char rcsid[] = "$Id: s.getrc.c 1.2 98/04/22 03:36:50-00:00 orc $";

/*
 * getrc() builds a list of services available for a specified runlevel.
 *
 * #include <ndialog.h>
 * usage:  getrc(char* runlevel, LIA* standard, LIA* custom)
 * 
 * the STANDARD services are the ones that are come bundled with the
 * system and can be found in /etc/rc.d/init.d.  CUSTOM services are
 * ones that are come from any other source (user intervention, automatic
 * install of non-Mastodon packages, and so on.)
 */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <ndialog.h>		/* for LIA definition */
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>

#include "getrc.h"

/*
 * notin() searches a LIA to see if a given service is already there
 */
static int
notin(char *rc, LIA list)
{
    int x;

    for (x=0; x < LIAcount(list); x++)
	if (strcmp(rc, LIAlist(list)[x].id) == 0)
	    return 0;
    return 1;
} /* notin */


/*
 * sortme() is a local function used by listsort()
 */
static int
sortme(ListItem *a, ListItem *b)
{
    return strcmp(a->item, b->item);
} /* sortme */


/*
 * listsort() sorts a LIA in alphabetical order.
 */
static void
listsort(LIA list)
{
    qsort(LIAlist(list), LIAcount(list), sizeof(ListItem), sortme);
    return;
} /* listsort */


/*
 * getinfo() does a `$rc info' call to the given service and returns
 * the first line (if any) of output, UNLESS that line of output starts
 * with `usage:'
 */
char*
getinfo(char *path)
{
    static char msg[200];
    FILE *f;

    sprintf(msg, "%s info 2>/dev/null", path);

    if ((f = popen(msg, "r")) != 0) {
	memset(msg, 0, sizeof msg);
	if (fgets(msg, sizeof msg, f))
	    strtok(msg, "\n");
	pclose(f);

	if (strncasecmp(msg, "usage:", 6) != 0 && msg[0] != 0)
	    return msg;
    }
    return 0;
} /* getinfo */


/*
 * goodpath() tells us if the path in a link points us at /etc/rc.d/init.d.
 * It only checks for absolute paths and ../init.d paths; if someone tries
 * to subvert it by doing FuNkY pAtHs, it will cheerfully allow itself to
 * be subverted.
 */
static int
goodpath(char *path)
{
    return (strncmp(RC_INIT_DIR, path, strlen(RC_INIT_DIR)) == 0)
	|| (strncmp(RC_INIT_LINK, path, strlen(RC_INIT_LINK)) == 0);
} /* goodpath */


/*
 * get the standard and custom RC files
 */
void
getrc(char runlevel, LIA* standard, LIA* custom)
{
    DIR* rcdir;
    struct dirent* rcfile;
    char* info;
    char path[100 + sizeof RC_PATH_PREFIX];
    char link[100 + sizeof RC_PATH_PREFIX];
    int must_be_custom;
    char* service;

    *standard = newLIA(0,0);
    *custom   = newLIA(0,0);

    /*
     * map the runlevel to lowercase 0, 1, 6, or M
     */
    if (!strchr("01m6", runlevel))
	runlevel = 'm';


    /* first read the active paths */
    sprintf(path, RC_PATH_PREFIX "%c", runlevel);
    if ((rcdir = opendir(path)) != 0) {
	while ((rcfile = readdir(rcdir)) != 0) {

	    if (rcfile->d_name[0] == '.') {
		must_be_custom = 1;
		service = 1 + rcfile->d_name;
	    }
	    else {
		must_be_custom = 0;
		service = rcfile->d_name;
	    }

	    /* we only care about files that start with lower-case letters;
	     * upper-case K and S files are SysV rc files, which we don't
	     * want to deal with.
	     */
	    if (! (isalpha(*service) && islower(*service)) )
		continue;
	    sprintf(path, RC_PATH_PREFIX "%c/%s", runlevel, rcfile->d_name);

	    info = getinfo(path);

	    if (must_be_custom || readlink(path, link, sizeof link) == -1
			       || !goodpath(link)) {
		addToLIA(*custom, service, info ? info : service, 0);
		LIAlist(*custom)[LIAcount(*custom)-1].selected = !must_be_custom;
	    }
	    else if (info) {
		addToLIA(*standard, service, info, 0);
		LIAlist(*standard)[LIAcount(*standard)-1].selected = 1;
	    }
	}
	closedir(rcdir);
    }

    /* then read the unselected rc scripts */
    if ((rcdir = opendir(RC_INIT_DIR)) != 0) {
	while ((rcfile = readdir(rcdir)) != 0) {
	    if (! (isalpha(rcfile->d_name[0]) && islower(rcfile->d_name[0])) )
		continue;

	    sprintf(path, RC_INIT_DIR "/%s", rcfile->d_name);

	    if ((info = getinfo(path)) == 0)
		continue;

	    if (notin(rcfile->d_name, *standard)) {
		addToLIA(*standard, rcfile->d_name, info, 0);
		LIAlist(*standard)[LIAcount(*standard)-1].selected = 0;
	    }
	}
	closedir(rcdir);
    }
    listsort(*standard);
    listsort(*custom);
} /* getrc */


#ifdef TEST
main()
{
    LIA standard, custom;
    int x;
    ListItem *thislist;

    getrc('m', &standard, &custom);

    if (LIAcount(standard) > 0) {
	puts("Standard RC files:");
	thislist = LIAlist(standard);
	for (x = 0; x < LIAcount(standard); x++)
	    printf("[%c] %s (%s)\n", thislist[x].selected ? 'X' : ' ',
				     thislist[x].item, thislist[x].id);
	deleteLIA(standard);
    }
    if (LIAcount(custom) > 0) {
	puts("Custom RC files:");
	thislist = LIAlist(custom);
	for (x = 0; x < LIAcount(custom); x++)
	    printf("[%c] %s (%s)\n", thislist[x].selected ? 'X' : ' ',
				     thislist[x].item, thislist[x].id);
	deleteLIA(custom);
    }
}
#endif
