/*
 * sniff: look for disks and install media.
 *
 * Copyright (c) 1997-1999 David L Parsons 
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by David L Parsons (orc@pell.chi.il.us).  My name may not be used
 * to endorse or promote products derived from this software without
 * specific prior written permission.  THIS SOFTWARE IS PROVIDED
 * ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.
 */

static const char rcsid[] = "$Id: s.sniff.c 1.14 99/11/24 01:00:01-00:00 orc $";

#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <sys/mount.h>
#include <linux/hdreg.h>
#include <linux/fs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <mntent.h>
#include <linux/cdrom.h>
#include <linux/hdreg.h>

#include <string.h>
#include <dialog.h>
#include <ndialog.h>

#include "partition.h"
#include "pathnames.h"
#include "util.h"
#include "builddisk.h"
#include "part.h"
#include "media.h"

struct idiskinfo Disk[] = {
    { "/dev/hda",ID_BOOTABLE },		/* 8 IDE devices */
    { "/dev/hdb" },
    { "/dev/hdc" },
    { "/dev/hdd" },
    { "/dev/hde" },
    { "/dev/hdf" },

    { "/dev/hdh" },
    { "/dev/sda",ID_SCSI|ID_BOOTABLE },	/* and 8 scsi disks */
    { "/dev/sdb",ID_SCSI },
    { "/dev/sdc",ID_SCSI },
    { "/dev/sdd",ID_SCSI },
    { "/dev/sde",ID_SCSI },
    { "/dev/sdf",ID_SCSI },
    { "/dev/sdg",ID_SCSI },
};
int NrDisk = (sizeof Disk/sizeof Disk[0]);

struct idiskinfo *InstallMedia = 0;
int NrMedia = 0;


static char *serialports[] = {
    "/dev/ttyS0",
    "/dev/ttyS1",
    "/dev/ttyS2",
    "/dev/ttyS3",
};
#define NR_SERIAL	(sizeof serialports / sizeof serialports[0])


void find_scsi_media(int *scsi_cd);

/*
 * addinstallmedia() adds an entry to InstallMedia
 */
static void
addinstallmedia(char *device, int extraflags)
{
    if (!InstallMedia)
	InstallMedia = malloc(1);
    InstallMedia = realloc(InstallMedia, (1+NrMedia) * sizeof InstallMedia[0]);

    if (extraflags & ID_REMOVABLE)
	extraflags &= ~ID_CDROM;
    else
	extraflags |= ID_CDROM;

    InstallMedia[NrMedia].device = strdup(device);
    InstallMedia[NrMedia].flags  = ID_EXIST|extraflags;
    InstallMedia[NrMedia].order  = NrMedia;
    InstallMedia[NrMedia].size   = 0;
    fprintf(stderr, "%d: %s on %s\n", NrMedia,
		     mediadesc(&InstallMedia[NrMedia]), device);
    NrMedia++;
} /* addinstallmedia */

/*
 * getmemsize() look at /proc and figure out memory sizes
 */
static unsigned long
getmemsize()
{
    struct stat st;

    return (stat("/proc/kcore", &st) == 0) ? st.st_size : 0;
} /* getmemsize */


/*
 * sniff() -- check all the popular devices, looking for filesystem fodder
 *            and possible installation media.
 */
int
sniff()
{
    int disksfound = 0;			/* how many disks have we found? */
    int scsi_cd = 0;
    int x;
    int fd;
    struct hd_driveid ideinfo;
    struct hd_geometry chs;
#if DEBUG
    char msg[200];
    char debug[80];
#endif
    extern int root_is_ide;

    diskC = EOF;		/* initialize all the globals we might want */
    mousetype = NO_MOUSE;

    /* detect the memory size */

    memsize = getmemsize();

    /* detect the mouse */

    if ((mousedev = detectPS2Mouse()) != 0)
	mousetype = PS2_MOUSE;
    else
	for (x=0; x < NR_SERIAL; x++)
	    if ((mousetype = detectSerialMouse(serialports[x])) != NO_MOUSE) {
		mousedev = serialports[x];
		break;
	    }

    if (mousetype != NO_MOUSE)
	mouse_was_reset = 1;

    /* detect disks and CD-ROM drive
     */

    find_scsi_media(&scsi_cd);

    for (x=0; x<NrDisk; x++,close(fd)) {
	Disk[x].flags |= ID_CHECKED;

	if ((fd = open(Disk[x].device, O_RDONLY|O_NDELAY)) >= 0) {
	    if (ioctl(fd, HDIO_GET_IDENTITY, &ideinfo) == 0) {
		/* check IDE devices; hope we don't trip over an IDE
		 * tape drive!
		 */
#if DEBUG
		snprintf(debug, sizeof debug,
				"%s: ideinfo.config %04x",
				Disk[x].device,
				ideinfo.config);
		dialog_notify(debug);
#endif
		if (strstr(ideinfo.model, "IOMEGA ZIP") != 0) {
		    /* zip drive:  not CD-ROM, but it's still removable
		     * media...
		     */
		    addinstallmedia(Disk[x].device, ID_REMOVABLE);
		    continue;
		}
		else if (ideinfo.config & 0x0080) {
		    addinstallmedia(Disk[x].device, 0);
		    continue;
		}
	    }
	    Disk[x].flags |= ID_EXIST;
	    if (ioctl(fd, HDIO_GETGEO, &chs) == 0)
		Disk[x].size = (chs.heads * chs.sectors * chs.cylinders)/2;

	    Disk[x].order = ++disksfound;
	    if (Disk[x].flags & ID_BOOTABLE) {
		if (diskC == EOF) {
		    diskC = x;
		    root_is_ide = (Disk[x].flags & ID_SCSI) == 0;
		}
	    }
	}
#if DEBUG
	else {
	    snprintf(debug, sizeof debug, "open %s: %s",
			Disk[x].device, strerror(errno));
	    dialog_notify(debug);
	}
#endif
    }
    for (x=0; x < scsi_cd; x++) {
	char device[20];

	sprintf(device, "/dev/scd%d", x);
	addinstallmedia(device, ID_SCSI);
    }

    return disksfound;
} /* sniff */


/*
 * find_scsi_media() looks at /proc and checks for any SCSI ZIP or CD-ROM
 *                   drives.  (tested with Linux 2.0.28 only; it may fail
 *                   horribly with other versions of linux, let alone other
 *                   Unices.)
 *
 * bugs: This depends on the format of /proc/scsi/scsi in linux 2.0.28.
 *       Since this format is likely to change, find_scsi_media() won't
 *       work on any systems aside from Linux 2.0.28.
 *       This also depends on Iomega, et al, arranging things so that
 *       ZIP drives will always identify as ZIP <something>.
 *
 *       If Linux had a devfs, this wouldn't be necessary.
 */
void
find_scsi_media(int *scsi_cd)
{
    FILE *f = fopen("/proc/scsi/scsi", "r");
    int diskno = 0;
    char line[200];

    if (f == 0)
	exit(0);

    fgets(line, sizeof line, f);	/* eat intro line */
    while (!feof(f)) {
	if (fgets(line, sizeof line, f) == 0)	/* eat Host: line */
	    break;
	if (fgets(line, sizeof line, f) == 0)	/* this is the Vendor info */
	    break;
	if (strstr(line, "Model: ZIP") != 0) {
	    /* we have a winnah */
	    if (4+diskno < NrDisk)
		Disk[4+diskno].flags |= ID_REMOVABLE;
	}
	if (fgets(line, sizeof line, f) == 0)	/* this is the media type */
	    break;
	if (strstr(line, "Direct-Access") != 0)
	    diskno++;
	else if (strstr(line, "CD-ROM") != 0)
	    (*scsi_cd)++;
    }
} /* find_scsi_media */
