2 ################################################################################
4 # File: ecrd: ECR Daemon
5 # Description: This script implements a daemon that handles requests for
6 # queries about information on ECRs contained in the Quintus
7 # database. In addition to lessoning the amount of time it takes
8 # for database opens, access to Quintus data is only available
9 # on certain machines. Additionally, for Perl to access this
10 # Informix database the Informix version of DBD would need to be
11 # locally installed. By calling this daemon instead clients need
12 # not have to install Informix and then code all the necessary
13 # code to access Quintus data as well as have to understand the
14 # structure of the database. Instead clients need only say "Give
15 # me what you got on ECR #<whatever>".
16 # Author: Andrew@DeFaria.com
17 # Created: Tue Feb 15 09:54:59 PST 2005
21 # (c) Copyright 2005, LynuxWorks, all rights reserved.
23 ################################################################################
32 my $ecrdb = "lynxmigr1";
33 my $port = (!defined $ENV {ECRDPORT}) ? 1500 : $ENV {ECRDPORT};
50 # ECR translations. Note the Quintus database stores certain choice lists as
51 # enumerations. They I guess they are configurable. The right thing to do
52 # would be to figure out how to look up the definition given the number. But
53 # we are gonna cheat here and hard code a few important enumerations.
89 my $me = `basename $0`;
91 my $ecrdversion = "1.3";
94 "productdefect", # integer
95 "componentdefect", # integer
96 "defectdefectdup", # integer
97 "workgroupdefect", # integer
98 "reporterdefect", # integer
99 "resolverdefect", # integer
100 "confirmerdefect", # integer
101 "buildversdefect", # integer
102 "rpt_versdefect", # integer
103 "res_versdefect", # integer
104 "conf_versdefect", # integer
106 "resolverstatus", # integer
107 "confirmerstatus", # integer
108 "escstatus", # integer
110 "severity", # integer
111 "priority", # integer
112 "summary", # varchar(80,0)
113 "datereported", # datetime year to second
114 "dateresolved", # datetime year to second
115 # "description", # text
116 # Note: Some descriptions fields are huge containing things like
117 # uuencoded tar files! They are so huge that they cause this server
118 # to fail (not sure why - it shouldn't but it does. So this hack
119 # returns only the first 50K of description to avoid that problem.
120 "description [1,50000]", # text
121 "cclist", # varchar(80,0)
122 "dateconfirmed", # datetime year to second
123 "datemodified", # datetime year to second
124 "fix_by_date", # date
125 "fix_by_version", # integer
127 "likelihood", # integer
128 "estfixtime", # datetime year to second
129 "actfixtime", # datetime year to second
131 "businessimpact", # integer
133 "docimpact", # integer
134 "report_platform", # integer
135 "resolve_platform", # integer
136 "confirm_platform", # integer
137 "test_file", # varchar(64,0)
138 "visibility", # integer
139 "misc", # varchar(80,0)
140 "defecttype", # integer
141 "defstatus", # integer
142 "customertext", # text
143 "modifiedby", # varchar(20,0)
144 "classification", # integer
145 "datefixed" # datetime year to second
153 my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
155 $mday = "0$mday" if $mday < 10;
156 $mon = "0$mon" if $mon < 10;
157 $hour = "0$hour" if $hour < 10;
158 $min = "0$min" if $min < 10;
161 return "$mon/$mday/$year $hour:$min";
165 print "[$pid] " . timestamp . " @_\n" if defined $verbose;
169 print STDERR "[$pid] " . timestamp . " ERROR: @_\n"
173 print STDERR "[$pid] " . timestamp . " WARNING: @_\n"
177 print "[$pid] " . timestamp . " @_\n" if defined $debug;
181 print "[$pid] " . timestamp . " @_\n" if !defined $quiet_mode;
186 my $statement = shift;
189 print "Catostrophic error: DB undefined!\n";
193 print $msg . "\nError #" . $DB->err . " " . $DB->errstr . "\n";
194 print "SQL Statement: $statement\n" if defined $statement;
200 debug "After $timeout seconds of inactivity client timed out";
202 my $hostinfo = gethostbyaddr ($ecrclient->peeraddr);
203 my $host = $hostinfo->name || $ecrclient->peerhost;
204 debug "Closing connection to $host";
206 # Close client's connection
209 # Set up signal handlers again
210 $SIG{ALRM} = \&timeout;
211 $SIG{INT} = $SIG{QUIT} = 23234
217 log_warning "Interrupted - closing down...";
219 verbose "Connection closed";
229 debug "ENTER: GetClientAck";
231 while (defined $client and defined ($clientresp = <$client>)) {
233 chop $clientresp if $clientresp =~ /\r/;
234 if ($clientresp eq "ACK") {
237 log_warning "Received $clientresp from client - expected ACK";
239 debug "EXIT: GetClientAck";
247 while (defined $client and defined ($clientresp = <$client>)) {
251 } # GetClientResponse
256 debug "ENTER: SendClientAck";
257 print $client "ACK\n";
258 debug "EXIT: SendClientAck";
261 sub SendClientResponse {
263 my $response = shift;
265 print $client "$response\n";
266 } # SendClientResponse
268 sub EnterDaemonMode {
270 my $errorlog = shift;
272 $logfile = "/dev/null" if $logfile eq "";
273 $errorlog = "/dev/null" if $errorlog eq "";
275 # Change the current directory to /
277 or die "$me: Error: Can't chdir to / ($!)";
282 # Redirect STDIN to /dev/null
283 open STDIN, '/dev/null'
284 or die "$me: Error: Can't redirect /dev/null ($!)";
286 # Redirect STDOUT to logfile
287 open STDOUT, ">>$logfile"
288 or die "$me: Error: Can't redirect stdout to $logfile ($!)";
290 # Redirect STDERR to errorlog
291 open STDERR, ">>$errorlog"
292 or die "$me: Error: Can't redirect stderr to $errorlog ($!)";
294 # Now fork the daemon
295 defined (my $pid = fork)
296 or die "$me: Error: Can't create daemon ($!)";
298 # Now the parent exits
301 # Set process to be session leader
303 or die "$me: Error: Can't start a new session ($!)";
307 # Connect to database. Note this is using anonymous access (read only)
308 $DB = DBI->connect("DBI:Informix:$ecrdb")
309 or DBError "Unable to open database";
310 log_message "Opened $ecrdb database";
312 # Setup our select statement with placeholders
313 $statement = "select ";
315 # Build up the field list
317 foreach (@all_fields) {
326 # Now add the table and condition
327 $statement .= " from defect where pkey=?";
329 $sth = $DB->prepare ($statement)
330 or DBError "Unable to prepare statement", $statement;
335 or DBError "Unable to disconnect from database!";
336 verbose "Closed $ecrdb database";
342 print "$msg\n\n" if defined $msg;
344 print "Usage: $me [ -D ] [ -v ] [ -d ] [-p <port>] [ -m ] [ -q ]\n\n";
345 print "Where:\t-D\tEnter Daemon mode\n";
346 print "\t-v\tVerbose mode (Default off)\n";
347 print "\t-d\tDebug mode (Default off)\n";
348 print "\t-p\tPort number to use (Default 1500)\n";
349 print "\t-m\tMultithreaded (Default off)\n";
350 print "\t-q\tQuiet mode (Default on)\n";
358 log_error "ECR $ecr is not numeric!";
367 or DBError "Unable to execute statement", $statement;
369 my $row = $sth->fetchrow_arrayref;
372 # @row is empty if there was no ECR by that number
373 log_error "ECR $ecr not found!";
378 foreach (@all_fields) {
379 my $value = shift @rows;
381 # Transform newlines to "\n" so the field is treated as one large field
382 $value =~ s/\n/\\n/g if defined $value;
384 # Perform some choice list field translations. Again this would be
385 # better done by doing database lookups to translate the enums...
386 $value = $defstatus [$value] if /defstatus/ and defined $value;
387 $value = $state [$value] if /state/ and defined $value;
388 $value = $priority [$value] if /priority/ and defined $value;
389 $value = $severity [$value] if /severity/ and defined $value;
390 # Fix description field back
391 if (/^description/) {
394 $fields {$_} = $value
401 my $ecrclient = shift;
403 # Service this client
404 my $hostinfo = gethostbyaddr ($ecrclient->peeraddr);
405 my $host = $hostinfo->name || $ecrclient->peerhost;
407 verbose "Connect from $host";
408 log_message "Waiting for command from $host";
410 GetClientAck ($ecrclient);
411 $_ = GetClientCmd ($ecrclient);
412 next unless /\S/; # Skip blank requests
413 last if /quit|exit/i;
416 log_message "$host requests a list of all ECR #'s";
417 SendClientAck ($ecrclient);
418 ReturnAllECRNbrs ($ecrclient);
419 SendClientAck ($ecrclient);
423 log_message "$host requests information about ECR $_";
424 SendClientAck ($ecrclient);
425 my %fields = GetECRRecord $_;
428 SendClientResponse ($ecrclient, "ecr: $_");
429 while (my ($key, $value) = each (%fields)) {
430 $value = !defined $value ? "" : $value;
431 SendClientResponse ($ecrclient, "$key: $value");
434 SendClientResponse ($ecrclient, "ECR $_ was not found");
436 SendClientAck ($ecrclient);
439 verbose "Closing connection from $host at client's request";
445 $SIG{CHLD} = \&Funeral;
446 log_message "Child has died" . ($? ? " with status $?" : "");
450 # Now wait for an incoming request
451 while ($ecrclient = $ecrserver->accept ()) {
452 my $hostinfo = gethostbyaddr ($ecrclient->peeraddr);
453 my $host = $hostinfo->name || $ecrclient->peerhost;
454 log_message "$host is requesting service";
455 if (defined ($multithreaded)) {
458 log_message "Spawning child to handle request";
460 die "$me: ERROR: Can't fork: %!" unless defined ($childpid = fork ());
463 # In parent - set up for clean up of child process
464 log_message "Parent produced child ($childpid)";
465 $SIG{CHLD} = \&Funeral;
466 log_message "Parent looking for another request to service";
468 # In child process - ServiceClient
470 debug "Child [$pid] has been born";
471 ServiceClient ($ecrclient);
472 log_message "Child finished servicing requests";
477 ServiceClient ($ecrclient);
484 sub ProcessRequests {
485 # The subroutine handles processing of requests by using a socket to
486 # communicate with clients.
487 $ecrserver = IO::Socket::INET->new (
494 die "$me: Error: Could not create socket ($!)\n" unless $ecrserver;
496 verbose "ECR DB Server (ecrd V$ecrdversion) accepting clients on port $port";
501 sub ReturnAllECRNbrs {
502 my $ecrclient = shift;
504 my $statement = "select pkey from defect";
506 my $sth = $DB->prepare ($statement)
507 or DBError "Unable to prepare statement", $statement;
510 or DBError "Unable to execute statement", $statement;
512 log_message "Returning all ECR numbers...";
513 while (my @row = $sth->fetchrow_array) {
514 SendClientResponse ($ecrclient, $row [0]);
517 log_message "All ECR numbers returned";
522 open STDOUT, ">-" or die "Unable to reopen STDOUT\n";
524 # Set unbuffered output
528 if ($ARGV [0] eq "-D") {
530 } elsif ($ARGV [0] eq "-v") {
533 } elsif ($ARGV [0] eq "-d") {
536 } elsif ($ARGV [0] eq "-m") {
538 } elsif ($ARGV [0] eq "-q") {
541 } elsif ($ARGV [0] eq "-p") {
543 Usage "Must specify a port # after -p" if (!defined $ARGV [0]);
546 Usage "Unknown parameter found: " . $ARGV[0];
552 my $tmp = (!defined $ENV {TMP}) ? "/tmp" : $ENV {TMP};
553 my $ecrd_logfile = "$tmp/$me.log";
554 my $ecrd_errfile = "$tmp/$me.err";
556 EnterDaemonMode ($ecrd_logfile, $ecrd_logfile) if defined ($daemon_mode);
560 # Set up signal handlers
561 $SIG{ALRM} = \&timeout;
562 $SIG{INT} = $SIG{QUIT} = \&interrupt;