/* $Id: dhcp.pc 4051 2011-09-08 18:46:37Z zacheiss $
 *
 * This generates the dhcpd.conf.print and associated files.
 *
 * Copyright (C) 1992-1998 by the Massachusetts Institute of Technology.
 * For copying and distribution information, please see the file
 * <mit-copyright.h>.
 */

#include <mit-copyright.h>
#include <moira.h>
#include <moira_site.h>

#include <sys/types.h>

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "util.h"

#define HWADDRMAP_CHWADDR_SIZE 18

EXEC SQL INCLUDE sqlca;

RCSID("$HeadURL: svn+ssh://svn.mit.edu/moira/trunk/moira/gen/dhcp.pc $ $Id: dhcp.pc 4051 2011-09-08 18:46:37Z zacheiss $");

char *whoami = "dhcp.gen";
char *db = "moira/moira";

void hwcolonify(char *from, char *to);

void sqlerr(void);

int main(int argc, char **argv)
{
  EXEC SQL BEGIN DECLARE SECTION;
  char name[MACHINE_NAME_SIZE];
  char hwaddr[HWADDRMAP_HWADDR_SIZE], chwaddr[HWADDRMAP_CHWADDR_SIZE];
  char ohwaddr[HWADDRMAP_HWADDR_SIZE], hwtype[PRINTERS_HWTYPE_SIZE];
  char addr[MACHINE_ADDRESS_SIZE], location[PRINTERS_LOCATION_SIZE];
  char contact[PRINTERS_CONTACT_SIZE], logaddr[MACHINE_ADDRESS_SIZE];
  char modtime[PRINTERS_MODTIME_SIZE], type[PRINTERS_TYPE_SIZE];
  char *unixtime_fmt = UNIXTIME_FMT;
  char host[MACHINE_ADDRESS_SIZE], types[SERVERHOSTS_VALUE3_SIZE];
  int mid, alcount, status;
  EXEC SQL END DECLARE SECTION;
  char shortname[MACHINE_NAME_SIZE], net[MACHINE_ADDRESS_SIZE];
  char filename[MAXPATHLEN];
  struct {
    char types[SERVERHOSTS_VALUE3_SIZE];
    char host[MACHINE_ADDRESS_SIZE];
  } *allowlist;
  char *p, *q;
  int i, allows, typelen;
  TARFILE *tf;
  FILE *out;
  time_t now = time(NULL);

  EXEC SQL CONNECT :db;

  EXEC SQL WHENEVER SQLERROR DO sqlerr();

  if (argc != 2)
    {
      fprintf(stderr, "usage: %s [outfile]\n", argv[0]);
      exit(MR_ARGS);
    }

  /* Get print spoolers for allow lists. */
  EXEC SQL SELECT COUNT(service) INTO :alcount FROM serverhosts sh, machine m
    WHERE (sh.service = 'PRINT' OR sh.service = 'CUPS-PRINT') 
    AND sh.mach_id = m.mach_id 
    AND (m.status = 1 OR m.status = 2) AND m.address != 'unassigned';
  allowlist = malloc(alcount * sizeof(*allowlist));

  EXEC SQL DECLARE csr_spool CURSOR FOR
    SELECT m.address, sh.value3 FROM machine m, serverhosts sh
    WHERE m.mach_id = sh.mach_id AND 
    (sh.service = 'PRINT' OR sh.service = 'CUPS-PRINT')
    AND (m.status = 1 OR m.status = 2) AND m.address != 'unassigned';
  EXEC SQL OPEN csr_spool;
  for (i = 0; i < alcount; i++)
    {
      EXEC SQL FETCH csr_spool INTO :host, :types;
      if (sqlca.sqlcode)
	sqlerr();
      strcpy(allowlist[i].host, strtrim(host));
      strcpy(allowlist[i].types, strtrim(types));
    }
  EXEC SQL CLOSE csr_spool;

  /* Now build the tar file. */
  tf = tarfile_open(argv[1]);

  /* Build dhcpd.conf.print */

  out = tarfile_start(tf, "/var/boot/dhcpd.conf.print", 0755, 0, 0,
		      "root", "root", now);
  ohwaddr[0] = '\0';
  EXEC SQL DECLARE csr_boot CURSOR FOR
    SELECT LOWER(m.name), hw.hwaddr, m.address, m2.address,
    pr.location, pr.contact, pr.hwtype
    FROM printers pr, machine m, machine m2, hwaddrmap hw
    WHERE pr.type != 'ALIAS' AND pr.mach_id != 0
    AND pr.mach_id = m.mach_id AND m.mach_id = hw.mach_id AND m.status != 3
    AND pr.loghost = m2.mach_id AND (pr.status = 1 OR pr.status = 2 OR pr.status = 4)
    ORDER BY hw.hwaddr, m.name;
  EXEC SQL OPEN csr_boot;
  while (1)
    {
      EXEC SQL FETCH csr_boot INTO :name, :hwaddr, :addr, :logaddr,
	:location, :contact, :hwtype;
      if (sqlca.sqlcode)
	break;

      strtrim(hwaddr);
      if (!*hwaddr || !strcasecmp(hwaddr, "unknown"))
	continue;
      if (!strcmp(hwaddr, ohwaddr))
	{
	  fprintf(stderr, "Ignoring duplicate hwaddr %s\n", hwaddr);
	  continue;
	}
      strcpy(ohwaddr, hwaddr);

      hwcolonify(hwaddr, chwaddr); 

      strtrim(name);
      strtrim(addr);
      strtrim(logaddr);
      strtrim(location);
      strtrim(contact);
      strtrim(hwtype);
      strcpy(shortname, name);
      if ((p = strchr(shortname, '.')))
	*p = '\0';

      if ((p = strchr(addr, '.')) && (q = strchr(++p, '.')))
	{
	  strncpy(net, p, q - p);
	  net[q - p] = '\0';
	}
      else
	continue;

      fprintf(out, "# %s: %s\n# contact: %s\nhost %s {\n\t"
	      "hardware ethernet %s;\n\tfixed-address %s;\n",
	      shortname, location, contact, hwaddr, chwaddr, addr);
      
      if (strlen(logaddr) != 0)
	      fprintf(out, "\toption log-servers %s;\n", logaddr);
	      
      if (!strncmp(hwtype, "HP", 2))
	fprintf(out, "\toption option-144 \"/hp/%s\";\n", shortname);
      fprintf(out, "}\n\n");
    }
  EXEC SQL CLOSE csr_boot;
  tarfile_end(tf);

  /* Now generate /var/boot/hp/ files */
  EXEC SQL DECLARE csr_boot2 CURSOR FOR
    SELECT LOWER(m.name), hw.hwaddr, m2.address, m3.address, pr.type,
    pr.location, pr.contact, TO_CHAR(pr.modtime, :unixtime_fmt), pr.status
    FROM printers pr, machine m, machine m2, machine m3, hwaddrmap hw
    WHERE pr.hwtype LIKE 'HP%' AND pr.mach_id != 0
    AND pr.mach_id = m.mach_id AND m.mach_id = hw.mach_id AND m.status != 3
    AND pr.rm = m2.mach_id AND pr.loghost = m3.mach_id AND pr.type != 'ALIAS'
    AND (pr.status = 1 OR pr.status = 2 OR pr.status = 4);
  EXEC SQL OPEN csr_boot2;
  while (1)
    {
      EXEC SQL FETCH csr_boot2 INTO :name, :hwaddr, :addr, :logaddr,
	:type, :location, :contact, :modtime, :status;
      if (sqlca.sqlcode)
	break;

      strtrim(hwaddr);
      if (!*hwaddr || !strcasecmp(hwaddr, "unknown"))
	continue;

      strtrim(name);
      strtrim(addr);
      strtrim(logaddr);
      strtrim(type);
      strtrim(location);
      strtrim(contact);
      strcpy(shortname, name);
      if ((p = strchr(shortname, '.')))
	*p = '\0';

      /* We create it as foo.new so dhcp.sh can append the passwords
       * and other data and rename it.
       */
      sprintf(filename, "/var/boot/hp/%s.new", shortname);
      out = tarfile_start(tf, filename, 0755, 0, 0, "root", "root",
			  unixtime(modtime));

      fprintf(out, "name: %s\nlocation: %s\ncontact: %s\n\n", shortname,
	      *location ? location : "unknown",
	      *contact ? contact : "unknown");

      if (*logaddr)
	fprintf(out, "trap-dest: %s\n", logaddr);

      /* Don't output any allow: lines for status 2 printers; they should be unrestricted. */
      if (status == 1 || status == 4)
	{
	  if (*logaddr)
	    fprintf(out, "allow: %s\n", logaddr);
	  fprintf(out, "allow: %s\n", addr);
	  typelen = strlen(type);
	  for (i = allows = 0; i < alcount && allows < 9; i++)
	    {
	      char *p;
	      
	      /* Don't list the spoolhost twice. */
	      if (!strcmp(allowlist[i].host, addr))
		continue;
	      
	      p = strstr(allowlist[i].types, type);
	      if (!p)
		continue;
	      
	      /* Make sure the match was real, and not just because one type
	       * is a substring of another type.
	       */
	      if (p != allowlist[i].types && *(p - 1) != ',' && *(p - 1) != ' ')
		continue;
	      p += typelen;
	      if (*p && *p != ',' && *p != ' ')
		continue;
	      
	      fprintf(out, "allow: %s\n", allowlist[i].host);
	      allows++;
	    }
	}
	 
      /* Rest of data is same for all printers and is appended from a
       * a file on the boot server.
       */

      tarfile_end(tf);
    }

  tarfile_close(tf);

  exit(MR_SUCCESS);
}

void hwcolonify(char *from, char *to)
{
  int f = 0, t = 0;
  int mod = 2;

  for (f = 0 ; f < HWADDRMAP_HWADDR_SIZE - 1 ; )
    {
      to[t++] = from[f++];
      if (f % mod == 0)
	to[t++] = ':';
    }
	
  if (f % mod == 0)
    t--;
  to[t] = '\0';
}

void sqlerr(void)
{
  db_error(sqlca.sqlcode);
}
