source: trunk/athena/etc/larvnetd/printer.c @ 14160

Revision 14160, 9.8 KB checked in by ghudson, 25 years ago (diff)
Fix a bug noticed by Aaron (hesiod_free_string() takes two arguments).
Line 
1/* Copyright 1998 by the Massachusetts Institute of Technology.
2 *
3 * Permission to use, copy, modify, and distribute this
4 * software and its documentation for any purpose and without
5 * fee is hereby granted, provided that the above copyright
6 * notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting
8 * documentation, and that the name of M.I.T. not be used in
9 * advertising or publicity pertaining to distribution of the
10 * software without specific, written prior permission.
11 * M.I.T. makes no representations about the suitability of
12 * this software for any purpose.  It is provided "as is"
13 * without express or implied warranty.
14 */
15
16/* This file is part of larvnetd, a monitoring server.  It implements
17 * functions for querying the printers for queue status information.
18 */
19
20static const char rcsid[] = "$Id: printer.c,v 1.7 2000-01-05 22:01:35 ghudson Exp $";
21
22#include <sys/types.h>
23#include <sys/socket.h>
24#include <netinet/in.h>
25#include <arpa/inet.h>
26#include <arpa/nameser.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <ctype.h>
31#include <unistd.h>
32#include <fcntl.h>
33#include <errno.h>
34#include <netdb.h>
35#include <syslog.h>
36#include <hesiod.h>
37#include "larvnetd.h"
38#include "timer.h"
39
40#define PRINTER_FALLBACK_PORT 515
41
42struct printer_poll_args {
43  struct serverstate *state;
44  struct printer *printer;
45};
46
47static void printer_poll(void *arg);
48static void printer_hes_callback(void *arg, int status, unsigned char *abuf,
49                                 int alen);
50static void printer_host_callback(void *arg, int status, struct hostent *host);
51static void *make_pargs(struct serverstate *state, struct printer *printer);
52
53void printer_start_polls(struct serverstate *state)
54{
55  int i;
56
57  syslog(LOG_DEBUG, "printer_start_polls");
58  for (i = 0; i < state->config.nprinters; i++)
59    printer_poll(make_pargs(state, &state->config.printers[i]));
60}
61
62static void printer_poll(void *arg)
63{
64  struct printer_poll_args *pargs = (struct printer_poll_args *) arg;
65  struct serverstate *state = pargs->state;
66  struct printer *printer = pargs->printer;
67  char *hesname;
68
69  /* Null out the timer, since it may have just gone off. */
70  printer->timer = NULL;
71  hesname = hesiod_to_bind(state->hescontext, printer->name, "pcap");
72  if (hesname == NULL)
73    {
74      syslog(LOG_ERR, "printer_poll: can't convert printer name %s to "
75             "hesiod name: %m", printer->name);
76      printer->timer = timer_set_rel(60, printer_poll, pargs);
77      return;
78    }
79
80  syslog(LOG_DEBUG, "printer_poll: printer %s starting query for %s",
81         printer->name, hesname);
82  ares_query(state->channel, hesname, C_IN, T_TXT, printer_hes_callback,
83             pargs);
84  hesiod_free_string(state->hescontext, hesname);
85}
86
87static void printer_hes_callback(void *arg, int status, unsigned char *abuf,
88                                 int alen)
89{
90  struct printer_poll_args *pargs = (struct printer_poll_args *) arg;
91  struct serverstate *state = pargs->state;
92  struct printer *printer = pargs->printer;
93  char **vec = NULL, *p, *q, *errmem;
94
95  if (status == ARES_EDESTRUCTION)
96    {
97      syslog(LOG_DEBUG, "printer_hes_callback: printer %s hesiod query "
98             "halted for channel destruction", printer->name);
99      free(pargs);
100      return;
101    }
102
103  if (status != ARES_SUCCESS)
104    {
105      syslog(LOG_ERR, "printer_hes_callback: could not resolve Hesiod pcap "
106             "for printer %s: %s", printer->name,
107             ares_strerror(status, &errmem));
108      ares_free_errmem(errmem);
109      goto failure;
110    }
111
112  /* Parse the result buffer into text records. */
113  vec = hesiod_parse_result(state->hescontext, abuf, alen);
114  if (!vec || !*vec)
115    {
116      syslog(LOG_ERR, "printer_hes_callback: could not parse Hesiod pcap "
117             "result for printer %s: %m", printer->name);
118      goto failure;
119    }
120
121  /* Look for the print server name. */
122  p = strchr(*vec, ':');
123  while (p)
124    {
125      if (strncmp(p + 1, "rm=", 3) == 0)
126        break;
127      p = strchr(p + 1, ':');
128    }
129  if (!p)
130    {
131      syslog(LOG_ERR, "printer_hes_callback: can't find print server name in "
132             "Hesiod pcap result for printer %s", printer->name);
133      goto failure;
134    }
135  p += 4;
136  q = p;
137  while (*q && *q != ':')
138    q++;
139  *q = 0;
140
141  syslog(LOG_DEBUG, "printer_hes_callback: printer %s starting query for %s",
142         printer->name, p);
143  ares_gethostbyname(state->channel, p, AF_INET, printer_host_callback, pargs);
144  hesiod_free_list(state->hescontext, vec);
145  return;
146
147failure:
148  if (vec)
149    hesiod_free_list(state->hescontext, vec);
150  printer->timer = timer_set_rel(60, printer_poll, pargs);
151}
152
153static void printer_host_callback(void *arg, int status, struct hostent *host)
154{
155  struct printer_poll_args *pargs = (struct printer_poll_args *) arg;
156  struct printer *printer = pargs->printer;
157  int s = -1, lport = IPPORT_RESERVED - 1, flags;
158  unsigned short port;
159  struct servent *servent;
160  struct sockaddr_in sin;
161  char *errmem;
162
163  if (status == ARES_EDESTRUCTION)
164    {
165      syslog(LOG_DEBUG, "printer_host_callback: printer %s hostname query "
166             "halted for channel destruction", printer->name);
167      free(pargs);
168      return;
169    }
170
171  if (status != ARES_SUCCESS)
172    {
173      syslog(LOG_ERR, "printer_host_callback: printer %s can't resolve print "
174             "server name: %s", printer->name, ares_strerror(status, &errmem));
175      ares_free_errmem(errmem);
176      goto failure;
177    }
178
179  s = rresvport(&lport);
180  if (s < 0)
181    {
182      syslog(LOG_ERR, "printer_host_callback: printer %s can't get reserved "
183             "port", printer->name);
184      goto failure;
185    }
186
187  /* Set s non-blocking so we can do a non-blocking connect. */
188  flags = fcntl(s, F_GETFL);
189  fcntl(s, F_SETFL, flags | O_NONBLOCK);
190
191  servent = getservbyname("printer", "tcp");
192  port = (servent) ? servent->s_port : htons(PRINTER_FALLBACK_PORT);
193
194  memset(&sin, 0, sizeof(sin));
195  sin.sin_family = AF_INET;
196  memcpy(&sin.sin_addr, host->h_addr, sizeof(sin.sin_addr));
197  sin.sin_port = port;
198  if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) == -1
199      && errno != EINPROGRESS)
200    {
201      syslog(LOG_ERR, "printer_host_callback: printer %s can't connect to "
202             "print server %s: %m", printer->name, host->h_name);
203      goto failure;
204    }
205
206  /* Set up the request we want to send; the main loop will call
207   * printer_handle_output() when the socket selects true for
208   * writing.
209   */
210  printer->s = s;
211  printer->to_send = 1;
212  sprintf(printer->buf, "\3%.*s\n", (int)(sizeof(printer->buf) - 3),
213          printer->name);
214  printer->buflen = strlen(printer->buf);
215  printer->jobs_counted = 0;
216  printer->up_so_far = 1;
217
218  syslog(LOG_DEBUG, "printer_host_callback: printer %s queued %d-byte query",
219         printer->name, printer->buflen);
220
221  free(pargs);
222  return;
223
224failure:
225  if (s >= 0)
226    close(s);
227  printer->timer = timer_set_rel(60, printer_poll, pargs);
228  return;
229}
230
231void printer_handle_output(struct serverstate *state, struct printer *printer)
232{
233  int count;
234
235  count = write(printer->s, printer->buf, printer->buflen);
236  if (count < 0)
237    {
238      syslog(LOG_ERR, "printer_handle_output: printer %s error writing to "
239             "print server: %m", printer->name);
240      close(printer->s);
241      printer->s = -1;
242      printer->timer = timer_set_rel(60, printer_poll,
243                                     make_pargs(state, printer));
244      return;
245    }
246  syslog(LOG_DEBUG, "printer_handle_output: printer %s wrote %d bytes",
247         printer->name, count);
248
249  /* Slide the data to send back in the buffer. */
250  printer->buflen -= count;
251  memmove(printer->buf, printer->buf + count, printer->buflen);
252
253  /* If we emptied the buffer, we're ready to receive. */
254  if (printer->buflen == 0)
255    {
256      syslog(LOG_DEBUG, "printer_handle_output: printer %s receiving",
257             printer->name);
258      printer->to_send = 0;
259    }
260}
261
262void printer_handle_input(struct serverstate *state, struct printer *printer)
263{
264  int count;
265  char *q;
266  const char *p;
267
268  /* Read in a chunk of data from the printer server. */
269  count = read(printer->s, printer->buf + printer->buflen,
270               sizeof(printer->buf) - printer->buflen);
271  if (count < 0)
272    {
273      syslog(LOG_ERR, "printer_handle_input: printer %s error reading from "
274             "print server: %m", printer->name);
275    }
276  if (count <= 0)
277    {
278      printer->jobs = printer->jobs_counted;
279      printer->up = printer->up_so_far;
280      close(printer->s);
281      printer->s = -1;
282      printer->timer = timer_set_rel(60, printer_poll,
283                                     make_pargs(state, printer));
284      syslog(LOG_DEBUG, "printer_handle_input: printer %s ending query, "
285             "jobs %d up %d", printer->name, printer->jobs, printer->up);
286      return;
287    }
288  printer->buflen += count;
289  syslog(LOG_DEBUG, "printer_handle_input: printer %s read %d bytes",
290         printer->name, count);
291
292  /* Now look over any complete lines we have. */
293  p = printer->buf;
294  q = memchr(p, '\n', printer->buflen);
295  while (q)
296    {
297      *q = 0;
298      syslog(LOG_DEBUG, "printer_handle_input: printer %s line: %s",
299             printer->name, p);
300      if (strncmp(p, "active ", 7) == 0 || isdigit((unsigned char)*p))
301        printer->jobs_counted++;
302      else if (strstr(p, "is down") || strstr(p, "Printer Error") ||
303               strstr(p, "ing disabled"))
304        printer->up_so_far = 0;
305      p = q + 1;
306      q = memchr(p, '\n', printer->buf + printer->buflen - p);
307    }
308
309  /* Copy any leftover partial line back into printer->buf. */
310  printer->buflen -= (p - printer->buf);
311  memmove(printer->buf, p, printer->buflen);
312
313  /* If the buffer is full and we don't have a complete line, toss the
314   * buffer so we can make some forward progress.  We don't expect
315   * this to happen when the print server is functioning correctly.
316   */
317  if (printer->buflen == sizeof(printer->buf))
318    {
319      syslog(LOG_NOTICE, "printer_handle_input: printer %s input buffer "
320             "full, flushing", printer->name);
321      printer->buflen = 0;
322    }
323}
324
325static void *make_pargs(struct serverstate *state, struct printer *printer)
326{
327  struct printer_poll_args *pargs;
328
329  pargs = emalloc(sizeof(struct printer_poll_args));
330  pargs->state = state;
331  pargs->printer = printer;
332  return pargs;
333}
Note: See TracBrowser for help on using the repository browser.