/* * Copyright (C) 2003 Robert Nowotniak */ /* * Ten maksymalnie prosty program (działający pod GNU/Linux) miał służyć do * przypominania o różnych rzeczach o określonych porach. Zastanawiałem się, w * jaki sposób powinno być to realizowane, aby działało pewnie i sprawnie. * Doszedłem do takich dwu wniosków: * * Nie wyobrażam sobie prostszego i bardziej rozbudowanego określania momentu * czasu niż to, jak jest to zrobione w crontabie. Stąd ten program bazuje na * funkcjonalności crona. * * Przypominanie powinno działać niezależnie od tego, co użytkownik robi w * danej chwili i niezależnie od tego, czy korzysta z X Window czy jest w * konsoli. (więc wszelkie Pop-Up'y X'owe na pewno odpadają). * * * Po uruchomieniu program otwiera sobie wirtualny terminal o wysokim numerze * (na pewno większym niż 12) i co pewnien okres czasu sprawdza, czy istnieje * dany plik. Jeśli tak, to oznacza, iż w danej chwili należy powiadomić * użytkownika o czymś. Treść wiadomości jest pobierana z pliku. * Tworzeniem pliku o odpowiednich porach zajmuje się cron. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern char **environ; #define PLIK "~/.automat/reminder" #define VTERMINAL 13 /* Który vt używać */ /* Uruchamiane w powłoce po utworzeniu vt */ /* Można tu wpisać na przykład ustawienie kodowania znaków */ #define PRECMDS \ "consolechars -f iso02.f16 -u iso02 -m iso02 ; echo -n \"\\033(U\"" #ifndef PATH_MAX #define PATH_MAX 255 #endif #define BUFOR_SIZE 255 int is_a_console(int fd); int open_a_console(char *fnam); int get_console_fd(char* tty_name); int zorganizuj_VT(void); void ustal_plik(void); void pokaz_wiadomosc(FILE *Plik); void Blad(const char *tresc); int zajety_vt(int vt); void ControlC(int p); void CheckMessage(int p); int listen_sig=0; int vtnr=VTERMINAL; char vtname[15]; /* /dev/ttyXXX */ char plikpath[PATH_MAX]; /* Numer poprzedniego vt */ int old_vt; /* Deskryptor konsoli */ int consfd; /* Funkcje używane do ustalenia rozmiarów okna komunikatu {{{ */ /* Sprawdza, ile jest linijek w pliku */ int ilelinii(FILE* plik) { char buf[BUFOR_SIZE]; int ret=0; int n,znaki; if( ! plik ) return(-1); while( (znaki=fread(buf, 1, BUFOR_SIZE-1, plik)) ) for(n=0; n ret ) ret = len; } /* Pominięcie '\n' */ --ret; rewind(plik); return ret; } /* }}} */ int main(int argc, const char **argv) { setlocale(LC_ALL, ""); ustal_plik(); zorganizuj_VT(); signal(SIGINT, ControlC); signal(SIGUSR1, CheckMessage); initscr(); start_color(); init_pair(1, COLOR_WHITE, COLOR_BLACK); init_pair(2, COLOR_WHITE, COLOR_RED); init_pair(3, COLOR_CYAN, COLOR_BLACK); curs_set(0); clear(); refresh(); noecho(); for(;;) { listen_sig=1; sleep(60); listen_sig=0; CheckMessage(-1); } endwin(); exit(EXIT_SUCCESS); } void ustal_plik(void) { glob_t GLOB1; struct stat stat1; bzero( &GLOB1, sizeof(glob_t) ); bzero( &stat1, sizeof(struct stat) ); glob(PLIK, GLOB_TILDE|GLOB_NOCHECK, NULL, &GLOB1); if( GLOB1.gl_pathc != 1 ) { fprintf(stderr,"%s wskazuje na więcej niż jeden plik.\n", PLIK); exit(EXIT_FAILURE); } if( ! stat(GLOB1.gl_pathv[0], &stat1) && ! S_ISREG(stat1.st_mode) ) { fprintf(stderr,"%s istnieje i nie jest to zwykły plik.\n", GLOB1.gl_pathv[0]); exit(EXIT_FAILURE); } strncpy( plikpath, GLOB1.gl_pathv[0], PATH_MAX-1 ); globfree(&GLOB1); if( unlink(plikpath)<0 && errno!=ENOENT ) { fprintf(stderr,"Nie dało się usunąć starego pliku %s.\n", plikpath); Blad("unlink()"); } } int zorganizuj_VT(void) { struct vt_stat vtstat; int fd; pid_t pid; /* * Nowy wirtualny terminal może mieć na przykład inną rozdzielczość (w przypadku FB), * dlatego dotychczasowe wartości $COLUMNS i $LINES nie powinny być brane pod uwagę. */ unsetenv("COLUMNS"); unsetenv("LINES"); sprintf(vtname, "/dev/tty%d", vtnr); if( (consfd=get_console_fd(NULL) < 0) ) { fprintf(stderr,"Błąd: Nie można znaleźć deskryptora konsoli.\n"); exit(EXIT_FAILURE); } if (ioctl(consfd, VT_GETSTATE, &vtstat) < 0) Blad("VT_GETSTATE"); if( zajety_vt(vtnr) || (vtstat.v_state & (1 << vtnr)) ) { fprintf(stderr, "Wirtualny terminal numer %d jest zajęty.\n", vtnr); exit(EXIT_FAILURE); } fd=open(vtname, O_RDWR); if( fd < 0 ) { fprintf(stderr, "Błąd: Nie można otworzyć %s: %s\n", vtname, strerror(errno)); exit(EXIT_FAILURE); } printf("Otwarto wirtualny terminal: %d\n", vtnr); old_vt = vtstat.v_active; pid=fork(); if( pid < 0 ) Blad("fork()"); if( pid ) exit(EXIT_SUCCESS); if( setsid() < 0 ) Blad("setsid()"); ioctl(fd, TIOCSCTTY, (char*)NULL); close(0); close(1); close(2); dup(fd); dup(fd); dup(fd); close(fd); #ifdef PRECMDS if( system(PRECMDS) < 0 ) { fprintf(stderr,"Błąd podczas próby uruchomienia %s :\n%s\n", PRECMDS, strerror(errno)); } #endif return 0; } /* Zwraca prawdę, jeśli dany terminal jest zajęty */ int zajety_vt(int vt) { FILE *Ps; char buf[80], tty[8]; int zajety=0; sprintf(tty, "tty%d", vt); Ps=popen("ps axo tt", "r"); while( fgets(buf, 79, Ps) ) if( strcasecmp(buf, buf) ) { zajety=1; break; } pclose(Ps); return zajety; } /* Funkcja wywołowana do wyświetlenia okienka wiadomości */ void pokaz_wiadomosc(FILE *Plik) { WINDOW *okno, *ramka; struct tm tm1; time_t tt; char bufor[BUFOR_SIZE], czasbuf[50]; int plik_lines, plik_cols; int y, x, wys, szer; int n; tt=time((time_t*)NULL); (void)localtime_r(&tt, &tm1); clear(); refresh(); mvprintw(0, 0, "Jest "); strftime(czasbuf, 49, "%A", &tm1); printw("%s, ", czasbuf); attron(COLOR_PAIR(3)); strftime(czasbuf, 49, "%d %B %Y", &tm1); printw("%s", czasbuf); attroff(COLOR_PAIR(3)); printw(" Godzina "); strftime(czasbuf, 49, "%H:%M", &tm1); attron(COLOR_PAIR(3)); printw("%s", czasbuf); attroff(COLOR_PAIR(3)); refresh(); plik_lines = ilelinii(Plik); plik_cols = ilekolumn(Plik); wys = plik_lines+4; if( wys > LINES-1 ) wys = LINES-1; szer = plik_cols+4; if ( szer > COLS ) szer=COLS; y = (LINES-wys)/2; if( y<1 ) y=1; x = (COLS-szer)/2; if( x<0 ) x=0; ramka=newwin(wys, szer, y, x); wborder(ramka, ' ' | COLOR_PAIR(2), ' ' | COLOR_PAIR(2), ' ' | COLOR_PAIR(2), ' ' | COLOR_PAIR(2), ' ' | COLOR_PAIR(2), ' ' | COLOR_PAIR(2), ' ' | COLOR_PAIR(2), ' ' | COLOR_PAIR(2)); wrefresh(ramka); wys-=4; szer-=4; x+=2; y+=2; okno=newwin(wys, szer, y, x ); while( (n=fread(bufor, 1, BUFOR_SIZE-1, Plik)) ) waddnstr(okno, bufor, n); wrefresh(okno); delwin(okno); delwin(ramka); } /* Funkcje pomocnicze z pakietu console-tools {{{ */ int is_a_console(int fd) { char arg; arg = 0; return (ioctl(fd, KDGKBTYPE, &arg) == 0 && ((arg == KB_101) || (arg == KB_84))); } int open_a_console(char *fnam) { int fd; fd = open(fnam, O_RDWR); if (fd < 0 && errno == EACCES) fd = open(fnam, O_RDONLY); if (fd < 0 && errno == EACCES) fd = open(fnam, O_WRONLY); if (fd < 0) return -1; if (! is_a_console(fd)) { close(fd); return -1; } /* Udało się otworzyć */ return fd; } int get_console_fd(char* tty_name) { int fd; if (tty_name) { if (-1 == (fd = open_a_console(tty_name))) return -1; else return fd; } fd = open_a_console("/dev/tty"); if (fd >= 0) return fd; fd = open_a_console("/dev/tty0"); if (fd >= 0) return fd; fd = open_a_console("/dev/console"); if (fd >= 0) return fd; for (fd = 0; fd < 3; fd++) if (is_a_console(fd)) return fd; fprintf(stderr, "Nie udało się znaleźć deskryptora, odnoszącego się do konsoli.\n"); return -1; } /* }}} */ void CheckMessage(int p) { FILE *Plik; struct stat plikstat; struct termios tio; struct vt_stat vtstat; struct timeval timeout; int n=0, old_vt; char buf[200]; fd_set fdi; if( p == SIGUSR1 && ! listen_sig ) return; if( ! stat(plikpath, &plikstat) ) { /* Plik istnieje */ Plik = fopen(plikpath, "r"); if( ! Plik ) Blad("fopen()"); pokaz_wiadomosc(Plik); fclose(Plik); if (ioctl(consfd, VT_GETSTATE, &vtstat) < 0) Blad("VT_GETSTATE"); old_vt = vtstat.v_active; /* Zmiana terminala */ if( ioctl(consfd, VT_ACTIVATE, vtnr) < 0 ) Blad("VT_ACTIVATE"); if( ioctl(consfd, VT_WAITACTIVE, vtnr) < 0 ) Blad("VT_ACTIVATE"); if( unlink(plikpath) ) Blad("unlink()"); curs_set(0); sleep(2); /* Zczytanie wszystkiego, co jest w buforze klawiatury */ def_prog_mode(); endwin(); tcgetattr(0,&tio); tio.c_lflag &= ~ICANON; tcsetattr(0,TCSANOW,&tio); do { ioctl(0,FIONREAD,&n); read(0,buf,n); } while( n > 0 ); tio.c_lflag |= ICANON; tcsetattr(0,TCSANOW,&tio); reset_prog_mode(); curs_set(0); #define PRESS_K_MSG " Naciśnij dowolny klawisz " /* Czekanie na klawisz */ mvhline(LINES-1,0,' ' | COLOR_PAIR(2),COLS); attron(A_BOLD); mvprintw(LINES-1, (COLS-strlen(PRESS_K_MSG))/2, PRESS_K_MSG); attroff(A_BOLD); refresh(); FD_ZERO(&fdi); FD_SET(0, &fdi); timeout.tv_sec=10; timeout.tv_usec=0; select(2, &fdi, NULL, NULL, &timeout); clear(); refresh(); if( ioctl(consfd, VT_ACTIVATE, old_vt) < 0 ) Blad("VT_ACTIVATE"); if( ioctl(consfd, VT_WAITACTIVE, old_vt) < 0 ) Blad("VT_ACTIVATE"); } else if( errno == ENOENT ) /* Plik nie istnieje */ return; else Blad("stat()"); return; } void ControlC(int p) { clear(); refresh(); endwin(); /* Zmiana terminala na poprzedni */ if( ioctl(consfd, VT_ACTIVATE, old_vt) < 0 ) Blad("VT_ACTIVATE"); if( ioctl(consfd, VT_WAITACTIVE, old_vt) < 0 ) Blad("VT_ACTIVATE"); /* Usunięcie obecnego terminala */ sprintf(vtname, "/dev/tty%d", vtnr+1); consfd=open(vtname, O_RDWR); if( consfd < 0 ) Blad("open()"); close(0); close(1); close(2); if( ioctl(consfd, VT_DISALLOCATE, vtnr) < 0 ) perror("VT_DISALLOCATE"); close(consfd); exit(EXIT_SUCCESS); } void Blad(const char *tresc) { perror(tresc); endwin(); exit(EXIT_FAILURE); } /* vim: set fdm=marker: */