
/* hwscan front end
   Copyright 2004 by SUSE (<adrian@suse.de>) */

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "init_message.h"

#define TIMEOUT 2
#define LONG_TIMEOUT 0
#define BUFFERS 1024

int main( int argc, char **argv )
{
        int ret, i;
	key_t key = KEY;
	int msgid;
	int mode = 0;
	int dev_nr = 0;
	int lines = 0;
	int block, usb, firewire, pci;
	int dev_last_state[BUFFERS];
	int dev_counter[BUFFERS];
	char * command_device[NR_COMMANDS][BUFFERS];
	time_t command_device_last[NR_COMMANDS][BUFFERS];
	time_t last;
	char **commands;
	char **devices;
	char buffer[32];
	message m;

	// are we running already, maybe ?
	{
		do {
			ssize_t r;
			char b[1024];
			char link[1024];
			int fd = open( PID_FILE, O_RDONLY );
			if ( fd >= 0 && (r=read(fd,b,1023)) > 0 ){
			        close(fd);
			        b[r]='\0';
			        snprintf(link, 1023, "/proc/%s/exe", b);
			        if ( (r=readlink( link, b, 1023 )) > 0 ){
			                b[r]='\0';
			                if ( r<8 )
			                        unlink(PID_FILE);
			                else if ( strcmp("/hwscand", b+strlen(b)-8) )
			                        unlink(PID_FILE);
					else
						exit(1);
			        }else
		                        unlink(PID_FILE);
		        }else if ( fd >= 0 )
	                        unlink(PID_FILE);
		} while ( 0 > (ret = open( PID_FILE, O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR ) ) );
		sprintf(buffer, "%d", getpid());
		if ( ret < 0 || write(ret,buffer,strlen(buffer)) <= 0 ){
			perror("hwscand: unable to write pid file "PID_FILE);
			exit(1);
		}
		close(ret);
	}

	// initialize ...
	for ( i=0; i<NR_COMMANDS; i++ ){
		command_device[i][0] = 0;
		command_device_last[i][0] = 1;
	}

	last=block=usb=firewire=pci=0;
	commands = (char**) malloc( BUFFERS * sizeof(char*) );
	devices  = (char**) malloc( BUFFERS * sizeof(char*) );

	msgid = msgget(key, IPC_CREAT | 0600);
        if (msgid < 0) {
		perror("msgget");
		exit(1);
        }

	while (1) {
		if ( last || dev_nr )
			mode = IPC_NOWAIT;
		else
			mode = 0;

		if( msgrcv(msgid, &m, MESSAGE_BUFFER, 1, mode) >= 0 ){
			char *p = m.mtext;

			if ( p == 0 ){
				fprintf( stderr, "hwscand: error, zero sized message\n" );
			}else{
				if ( p[0] == 'S' && strlen(p) > 1 ){
					// scan calls
					char z[2];
					int c;
					z[0] = *(p+1);
					z[1] = '\0';
					c = atoi(z);
					if ( c < NR_COMMANDS ){
						if ( ! command_with_device[c] ){
							last = time(0L);
							if ( LONG_TIMEOUT+command_device_last[c][0] < time(0L) )
								command_device_last[c][0] = 0;
						}else
						for ( i=0; i<BUFFERS; i++ ){
							if ( !command_device[c][i] ){
								last = time(0L);
								command_device[c][i] = strdup(p+2);
								command_device[c][i+1] = 0;
								command_device_last[c][i] = 0;
								break;
							}else if ( !strcmp(command_device[c][i], p+2) ){
								last = time(0L);
								if ( LONG_TIMEOUT+command_device_last[c][i] < time(0L) )
									command_device_last[c][i] = 0;
								break;
							}
						}
					}
				}
				if ( p[0] == 'C' && lines < BUFFERS ){
					last = time(0L);
					// config calls
					commands[lines] = strdup(p+1);
					lines++;
				}
				if ( p[0] == 'A' && dev_nr < BUFFERS ){
					// add scan devices
					devices[dev_nr]        = strdup(p+1);
					dev_last_state[dev_nr] = 0;
					dev_counter[dev_nr]    = 0;
					dev_nr++;
				}
				if ( p[0] == 'R' && dev_nr < BUFFERS ){
					for ( i=0; i<dev_nr; i++ ){
						if ( !strcmp(p+1, devices[i]) ){
							int j;
							free(devices[i]);
							for ( j=i; j+1<dev_nr; j++ ){
								devices[j]        = devices[j+1];
								dev_last_state[j] = dev_last_state[j+1];
								dev_counter[j]    = dev_counter[j+1];
							}
							dev_nr--;
						}
					}
				}
			}
#if DEBUG
				printf("CALL RECEIVED %s\n", p);
#endif
		}else{
			// we do this only in scanning mode ...

			sleep(1);
			for ( i=0; i<dev_nr; i++ ){
				if (dev_counter[i]<0) continue;
				dev_counter[i]++;
				if ( dev_counter[i] > 5 ){
					int fd;
					char buf[MESSAGE_BUFFER];
					dev_counter[i] = 0;
					fd = open( devices[i], O_RDONLY );
					strcpy( buf, "/sbin/hwscan --fast --partition --only=");
					strcat( buf, devices[i] );
					if ( fd < 0 ){
						if ( dev_last_state[i] )
							system(buf);
						dev_last_state[i] = 0;
					}else{
						if ( dev_last_state[i] == 0)
							system(buf);
						dev_last_state[i] = 1;
						close(fd);
					}
				}
			}
		}

		if ( last && (last+TIMEOUT <= time(0L)) ){
			char buf[MESSAGE_BUFFER * NR_COMMANDS];
			int run_really = 0;

			last=0;
			strcpy( buf, "/sbin/hwscan --fast --boot --silent" );
			for ( i=0; i<NR_COMMANDS; i++ ){
				if ( command_with_device[i] == 0 &&
				     command_device_last[i][0] == 0 ){
					command_device_last[i][0] = time(0L);
					strcat( buf, " --");
					strcat( buf, command_args[i] );
				 	run_really = 1;
				} else {
					int j;
					int commappended = 0;

					for ( j=0; j<BUFFERS; j++ ){
						if ( !command_device[i][j] )
							break;
						if ( command_device_last[i][j] == 0 ){
							if (!commappended) {
								strcat( buf, " --");
								strcat( buf, command_args[i] );
								commappended = 1;
						        }
							strcat( buf, " --only=" );
							strcat( buf, command_device[i][j] );
							command_device_last[i][j] = time(0L);
							run_really = 1;
							if (strlen(buf) > sizeof(buf) - MESSAGE_BUFFER)
								break;
						}
					}
				}
				if (strlen(buf) > sizeof(buf) - MESSAGE_BUFFER) {
					last = time(0L);	/* call me again */
					break;
				}
			}

			if ( run_really ){
#if DEBUG
				printf("RUN %s\n", buf);
#endif
				system(buf);
#if DEBUG
				printf("RUN quit %s\n", buf);
#endif
			}
			if ( lines ){
				for (i=0; i<lines; i++){
#if DEBUG
				printf("CALL DIRECT %s\n", commands[i]);
#endif
					system(commands[i]);
#if DEBUG
				printf("CALL quit %s\n", commands[i]);
#endif
					free(commands[i]);
				}
				lines=0;
			}
		}
	}

	return 0;
}
