/* * smsserver.c ... sending and receiving SMS messages from an embedded linux computer like Foxboard or Raspberry Pi * * Copyright (C) 2007-2013 Jiri Pittner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * */ #undef debug //@@@some sort of status besides directecho - ifconfig result for example //@@@user input of AT commands from fifofile //on G20 phone is at ttyS4, power NEGATED is at gpio38, startpulse is at gpio41 //needs -static due to setjmp/longjmp #ifdef G20 #define POWERPIN 38 #define PULSEPIN 41 #endif //GPIO 4 switches power to the USB 3G stick (by external circuitry added to RPI) #ifdef RPI #define POWERPIN 4 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef emulate #ifdef CRIS #include #endif #endif #include #include #define BUFL 1024 #define MAXLINE 4096 //porting from atmel #define PSTR(X) X #define strcpy_P strcpy #define strcat_P strcat #define itoa10(n,s) sprintf(s,"%d",n) //GSM requires #define MAXSMS 161 #define MAXPDU (MAXSMS*7/8*2+32)+128 #define CELL_ID_LENGTH 4 #define OPER_ID_LENGTH 7 #define MAXLIST (20*MAXPDU) void xerror(error_text) char error_text[]; { fprintf(stderr,"smsserver: %s\n",error_text); exit(10); } void delay_cs(unsigned int n) { struct timespec t; t.tv_sec=n/100; t.tv_nsec=(n-100*t.tv_sec)*10000000; nanosleep(&t,NULL); } struct termios devicetermios0, ttytermios0; int filehandle; void termioscleanup() { tcsetattr(fileno(stdin),TCSANOW,&ttytermios0); tcsetattr(filehandle,TCSANOW,&devicetermios0); } void usage() { exit(1); } static int phone_fd; static jmp_buf env,env2; static void myjump() {longjmp(env,1);} static void myjump2() {longjmp(env2,1);} static void myignore(){} static FILE *logfilepointer; static unsigned int phone_not_on=1; int ring=0; int communicate(char *buf,char *bufend, int cs) //return 0 on success 1 on timeout, 2 on error, 3 on RING, 4 on IO error { uint8_t status, x,y,z,w; char *buf0=buf; int r; int t=0; static int writefailed=0; #ifdef debug fprintf(logfilepointer,"DEBUG: communicate(timeout %d): %s\n",cs,buf); fflush(logfilepointer); #endif if(cs<0) cs= -cs; if(cs<20) cs=20; //read all unsolicited garbage { char garbage[MAXLINE]; fd_set readfds; int s; fd_set writefds; fd_set exceptfds; FD_ZERO(&writefds); FD_ZERO(&exceptfds); FD_ZERO(&readfds); FD_SET(phone_fd,&readfds); struct timeval timeout; timeout.tv_sec= 0; timeout.tv_usec= 1000; do{ if((s=select(phone_fd+1,&readfds,&writefds,&exceptfds,&timeout)) == -1) { fprintf(logfilepointer,"select(communicate0) return code -1: %s \n",strerror(errno)); fflush(logfilepointer); } if(FD_ISSET(phone_fd,&readfds)) { int r=read(phone_fd,garbage,MAXLINE-1); if(r>0) ring= strstr(garbage,"RING")?1:0; #ifdef debug //fprintf(stderr,"discarded %s\n",garbage); #endif } } while(FD_ISSET(phone_fd,&readfds)); } r=write(phone_fd,buf,strlen(buf)); if(r!=strlen(buf)) { if(!writefailed) fprintf(logfilepointer,"write to phone failed\n"); writefailed += 1; struct stat buf; if(writefailed>1000 && stat("/tmp/smsserver.fail",&buf)!=0) { FILE *f = fopen("/tmp/smsserver.fail","w"); if(f) fclose(f); } return 4; } else {unlink("/tmp/smsserver.fail"); writefailed=0;} buf[0]=0; status=0; if(cs) { struct itimerval itv; memset((void *)&itv,0,sizeof(itv)); itv.it_value.tv_sec = cs/100; itv.it_value.tv_usec = 10000*(cs%100); if(SIG_ERR == signal(SIGALRM,myjump)) {fprintf(logfilepointer,"signal failed\n"); return 6;}; if(0!= setitimer(ITIMER_REAL, &itv, NULL)) {signal(SIGALRM,SIG_IGN); fprintf(logfilepointer,"setitimer failed\n"); return 5;} } int setjmp_status=setjmp(env); #ifdef debug if(setjmp_status) fprintf(logfilepointer,"DEBUG: setjmp %d\n",setjmp_status); fflush(logfilepointer); #endif if(!setjmp_status) { do { buf+=strlen(buf); { int s; fd_set readfds; fd_set writefds; fd_set exceptfds; FD_ZERO(&writefds); FD_ZERO(&exceptfds); FD_ZERO(&readfds); FD_SET(phone_fd,&readfds); struct timeval timeout; timeout.tv_sec= (cs-10)/100; timeout.tv_usec= 10000*((cs-10) %100); if((s=select(phone_fd+1,&readfds,&writefds,&exceptfds,&timeout)) == -1) { fprintf(logfilepointer,"select(communicate) return code -1: %s \n",strerror(errno)); fflush(logfilepointer); signal(SIGALRM,SIG_IGN); return(1); //caught a signal during select - behave like a timeout } if(FD_ISSET(phone_fd,&readfds)) { if(bufend-buf-1==0) {fprintf(logfilepointer,"buffer exceeded error\n"); return 7;} r=read(phone_fd,buf,bufend-buf-1); //NOTE: this read can block and somehow the signal was sometimes missed, so we added the select() above #ifdef debug // fprintf(logfilepointer,"DEBUG: read returned %d\n",r); #endif if(r<0) { fprintf(logfilepointer,"read from phone failed\n"); signal(SIGALRM,SIG_IGN); return 4; } buf[r]=0; //scan() in atmel did it, but read() does not! } else {signal(SIGALRM,SIG_IGN); longjmp(env,2);}//timeout } #ifdef debug fprintf(logfilepointer,"DEBUG: r=%d %s\n",r,buf); fflush(logfilepointer); #endif w= !strstr(buf,">"); z= !strstr(buf,"OK"); x= !strstr(buf,"ERROR"); y= !strstr(buf,"RING") && !strstr(buf,"BUSY"); delay_cs(100); if(r==0) ++t; //if read returns repeatedly 0 timeout if(t>60) goto do_timeout; } while(z && x && y && w); //break on sms-prompt or termination if(x==0) status=2; //error if(y==0) status=3; //ring if(z==0 || w==0) status=0; //OK #ifdef debug fprintf(logfilepointer,"DEBUG From phone: %s\n",buf0); fflush(logfilepointer); #endif } else { do_timeout: status=1; //timeout phone_not_on += 1; fprintf(logfilepointer,"Phone not answering: timeout\n"); fflush(stderr); } signal(SIGALRM,SIG_IGN); //switch off deadline timer //to be sure that phone is ready to receive new data if(status!=1) delay_cs(20); return status; } void __attribute__((weak)) phone_on(int onoff) { } #ifdef RPI void phone_on(int onoff) { //there is a watchdog script for this //usb_modeswitch -s 3 -v 0x1bbb -p 0xf000 -V 0x1bbb -P 0x00b7 -M 5553424308a05984c000000080000606f50402527000000000000000000000 //modprobe usbserial vendor=0x1bbb product=0x00b7 should have been done at boot time } #endif #ifdef G20 #define GPIODIR2(N) "/sys/class/gpio/gpio" #N "/direction" #define GPIOVAL2(N) "/sys/class/gpio/gpio" #N "/value" //due to lack of recursion in preprocessor #define GPIODIR(N) GPIODIR2(N) #define GPIOVAL(N) GPIOVAL2(N) void phone_on(int onoff) { int fdg; int i=0; FILE *f; if ((f = fopen("/sys/class/gpio/export", "w")) == NULL) { perror("cannot open /sys/class/gpio/export"); return ; } fprintf(f,"%d\n",POWERPIN); fflush(f); fprintf(f,"%d\n",PULSEPIN); fflush(f); fclose(f); if ((f = fopen(GPIODIR(POWERPIN), "w")) == NULL) { perror("cannot open direction file"); return ; } fprintf(f,"out\n"); fclose(f); if ((f = fopen(GPIODIR(PULSEPIN), "w")) == NULL) { perror("cannot open direction file"); return ; } fprintf(f,"out\n"); fclose(f); int pulse=open(GPIOVAL(PULSEPIN),O_WRONLY); if(pulse<0) {perror(GPIOVAL(PULSEPIN)); return;} int power = open(GPIOVAL(POWERPIN),O_WRONLY); if(pulse<0) {perror(GPIOVAL(POWERPIN)); return;} if(onoff) { if(2!=write(power,"1\n",2)) {perror("cannot write to gpio"); return;} sleep(2); if(2!=write(power,"0\n",2)) {perror("cannot write to gpio"); return;} //on sleep(2); if(2!=write(pulse,"1\n",2)) {perror("cannot write to gpio"); return;} sleep(2); if(2!=write(pulse,"0\n",2)) {perror("cannot write to gpio"); return;} sleep(2); } else { if(2!=write(power,"1\n",2)) {perror("cannot write to gpio"); return;} } close(power); close(pulse); } #endif #ifdef CRIS void phone_on(int onoff) { fprintf(logfilepointer,"Switching phone %d\n",onoff); fflush(logfilepointer); #ifndef emulate int fdg; int iomask_g; int i=0; if ((fdg = open("/dev/gpiog", O_RDWR))<0) { fprintf(stderr,"ERROR /dev/gpiog\n"); return ; } iomask_g = (1<<23)| (1<<21); ioctl(fdg, _IO(ETRAXGPIO_IOCTYPE, IO_SETOUTPUT),&iomask_g); int r; if(onoff) { r=ioctl(fdg, _IO(ETRAXGPIO_IOCTYPE, IO_SETBITS),(1<<21)); //power off sleep(3); r=ioctl(fdg, _IO(ETRAXGPIO_IOCTYPE, IO_CLRBITS),(1<<21)); //power on sleep(1); r=ioctl(fdg, _IO(ETRAXGPIO_IOCTYPE, IO_SETBITS),(1<<23)); //start pulse sleep(1); r=ioctl(fdg, _IO(ETRAXGPIO_IOCTYPE, IO_CLRBITS),(1<<23)); //stop pulse sleep(2); r=ioctl(fdg, _IO(ETRAXGPIO_IOCTYPE, IO_SETBITS),(1<<23)); //start pulse sleep(1); r=ioctl(fdg, _IO(ETRAXGPIO_IOCTYPE, IO_CLRBITS),(1<<23)); //stop pulse sleep(1); } else { r=ioctl(fdg, _IO(ETRAXGPIO_IOCTYPE, IO_SETBITS),(1<<21)); //power off } close(fdg); #endif phone_not_on = !onoff; } #endif void phone_reset(void) { char buf[BUFL]; strcpy_P(buf,PSTR("AT\r")); communicate(buf,buf+BUFL,150); strcpy_P(buf,PSTR("ATZ\r")); communicate(buf,buf+BUFL,150); strcpy_P(buf,PSTR("ATZ\r")); { uint8_t r; r=communicate(buf,buf+BUFL,150); //if timeout mark phone as off if(r==1) {phone_not_on=1; return;} } delay_cs(1000); //some AT commands report error until the phone is connected to the network //we will poll, do not use incoming message notification //strcpy_P(buf,PSTR("AT+CNMI=1,1,0,0,1\r")); communicate(buf,buf+BUFL,200); } void get_location(char *text) { char buf[BUFL],*p, *q; strcpy_P(buf,PSTR("AT+COPS?\r")); communicate(buf,buf+BUFL,100); p=strstr(buf,"COPS:")+5; p=strchr(p,'"')+1; q=strchr(p,'"'); *q=0; strncpy(text,p,OPER_ID_LENGTH); text[OPER_ID_LENGTH]=0; //ericsson_A2628 does not give cell id in +creg and it is thus useless #if !defined (ericsson_a2618s) strcat_P(text,PSTR(":")); text+=strlen(text); #if defined( nokia_6310i) || defined (siemens_c45) strcpy_P(buf,PSTR("AT+CREG=2\r")); communicate(buf,buf+BUFL,100); #endif strcpy_P(buf,PSTR("AT+CREG?\r")); communicate(buf,buf+BUFL,100); p=strstr(buf,"CREG:")+5; p=strrchr(p,',')+2; strncpy(text,p,CELL_ID_LENGTH); text[CELL_ID_LENGTH]=0; #endif } uint8_t get_number(uint8_t loc, char *number, char *label) { char buf[BUFL],*p,*q; strcpy_P(buf,PSTR("AT+CPBR=")); itoa10(loc,buf+strlen(buf)); strcat_P(buf,PSTR("\r")); communicate(buf,buf+BUFL,100); p=strchr(buf,','); if(!p) return 1; p+=2; if(*p == '+') ++p; //skip international + if relevant q=strchr(p,'"'); *q=0; strcpy(number,p); if(label) { p=strchr(q+2,','); if(!p) *label=0; else { p+=2; q=strchr(p,'"'); *q=0; strcpy(label,p); } } return 0; } uint8_t put_number(uint8_t loc, char *number, char *label) { char buf[BUFL],*p,*q; strcpy_P(buf,PSTR("AT+CPBW=")); itoa10(loc,buf+strlen(buf)); strcat_P(buf,PSTR(",\"")); strcat(buf,number);//number contains + strcat_P(buf,PSTR("\",145,\"")); strcat(buf,label); strcat_P(buf,PSTR("\"\r")); return communicate(buf,buf+BUFL,100); } /*PDU-coding and SMS-specific routines*/ uint8_t letter2bin (char c) { return c>'9' ? c+10-(c>='a'?'a':'A') : c-'0'; } uint8_t octet2bin(char* octet) { return (letter2bin(octet[0])<<4) | letter2bin(octet[1]); } void bin2letter(char *c, uint8_t b) { *c = b<10? '0'+b : 'A'+b-10; } void bin2octet(char *octet, uint8_t bin) { bin2letter(octet,bin>>4); bin2letter(octet+1,bin&0x0f); } void pdu2ascii(char* pdu0, char* ascii) /* converts a PDU-String to Ascii */ { /* the first octet is the length of the ascii */ uint8_t nremain,i,*binary; uint16_t len,count; char *pdu; //hexa to binary conversion count=octet2bin(pdu0); pdu=pdu0+2; len=strlen(pdu); if((count<<3)-count> (len<<2)) {strcpy_P(ascii,PSTR("ERROR:unexpected end of PDU")); return;} binary=(uint8_t *) pdu0; while(*pdu) { *binary++ =octet2bin(pdu); pdu+=2; } /* now convert from 8-Bit to 7-Bit */ binary=(uint8_t *)pdu0 -1; nremain=0; for(i=0;i 0) //some bits in current binary { ascii[i] |= (*binary>>(8-nremain)); } if(nremain < 7) //we need a new binary for this char { ascii[i] |= *++binary < len/2) {strcpy(ascii0,"unexpected end of PDU\n"); return;} char *ascii=ascii0; while(*pdu) { uint8_t c=octet2bin(pdu); if(c) *ascii++ =c; //discard zero bytes of wide chars pdu+=2; } *ascii=0; } void swapchars(char* string) /* Swaps every second character, argument must have even length */ { char c; while((c=string[0])) { string[0]=string[1]; string[1]=c; string+=2; } } void decodepdu(char *ascii, char *phone, char *date, char *pdu0) { uint8_t i; char *pdu=pdu0; //skip SMS center info i=octet2bin(pdu); pdu+=(i+2)<<1; //decode sender phone number i=octet2bin(pdu); strncpy(phone,pdu+4,i); phone[i]=0; swapchars(phone); if(i&1) {++i; phone[i-1]=0;} pdu+=i+6; uint8_t dcs=octet2bin(pdu); pdu+=2; strncpy(date,pdu,12); date[12]=0; swapchars(date); pdu+=14; if(dcs & 12 ) pdu2wide(pdu,ascii,dcs); else pdu2ascii(pdu,ascii); } void ascii2pdu(char* ascii,char* pdu) { uint8_t *tmp,*tmp0; uint8_t nfree; char c; //message length bin2octet(pdu,strlen(ascii)); pdu+=2; //bit shifts, rewrite into ascii to save space nfree=0; tmp0=(uint8_t *)ascii; tmp=tmp0-1; while((c= *ascii++)) { //part of ascii which goes to current byte if(nfree>0) { *tmp |= c<<(8-nfree); } //part of ascii which goes to next byte if(nfree<7) { *++tmp = (c&0x7f) >> nfree; } nfree=(1+nfree)&7; } //print in hexa for (; tmp0<=tmp; ++tmp0, pdu+=2) bin2octet(pdu,*tmp0); pdu[0]=0; } /* make the PDU string. The destination variable pdu has to be big enough. */ void makepdu(char* message, char* nummer, char* pdu, uint8_t numtype) { uint8_t l; //header strcpy_P(pdu,PSTR("001100")); pdu+=6; //phone number l=strlen(nummer); bin2octet(pdu,l); pdu+=2; bin2octet(pdu,numtype); pdu+=2; strcpy(pdu,nummer); if (l&1) strcat_P(pdu,PSTR("F")); swapchars(pdu); //some other constant garbage strcat_P(pdu,PSTR("00F1A7")); //message length and text ascii2pdu(message,pdu+strlen(pdu)); } int read_sms(char *number, char *ascii, uint8_t delete, char *pdu_debug) { char pdu[MAXLIST]; char *p; uint8_t loc; strcpy_P(pdu,PSTR("AT+CMGL=4\r")); communicate(pdu,pdu+MAXPDU,7000); //do not wait for OK, it might be after too a long text, switch off watchdog if ((p=strstr(pdu,"+CMGL:"))) { p+=6; loc=atoi(p); p=strchr(p,'\n')+1; //terminate the PDU if several messages are contained char *q=strstr(p,"+CMGL:"); if(q) *q=0; if(pdu_debug) strncpy(pdu_debug,p,MAXPDU); char date[16]; decodepdu(ascii,number,date,p); } else return 1; //delete after it has been read if(delete) { delay_cs(80); strcpy_P(pdu,PSTR("AT\r")); communicate(pdu,pdu+MAXPDU,100); delay_cs(80); strcpy_P(pdu,PSTR("AT\r")); communicate(pdu,pdu+MAXPDU,100); strcpy_P(pdu,PSTR("AT+CMGD=")); itoa10(loc,pdu+strlen(pdu)); strcat_P(pdu,PSTR("\r")); communicate(pdu,pdu+MAXPDU,300); delay_cs(80); } return 0; } void dial(char *number) { char buf[BUFL]; uint8_t i; strcpy_P(buf,PSTR("AT+CBST=7,0,1\r")); //suitable for calling both gsm and fixed network lines communicate(buf,buf+BUFL,100); strcpy_P(buf,PSTR("ATD+")); strcat(buf,number); strcat_P(buf,PSTR("\r")); communicate(buf,buf+BUFL,6000); delay_cs(500); //wait another 5 seconds strcpy_P(buf,PSTR("+++ATH\r")); communicate(buf,buf+BUFL,100); strcpy_P(buf,PSTR("ATZ\r")); communicate(buf,buf+BUFL,100); } int send_sms(char *number, char *ascii0) //its argument ascii will be overwritten { char buf[BUFL], pdu[MAXPDU], ascii[MAXSMS]; uint8_t r; strncpy(ascii,ascii0,MAXSMS-1); ascii[MAXSMS-1]=0; makepdu(ascii,number,pdu,0x91); strcpy_P(buf,PSTR("AT+CMGS=")); itoa10(strlen(pdu)/2-1,buf+strlen(buf)); strcat_P(buf,PSTR("\r")); r=communicate(buf,buf+BUFL,350); { char ctrlz[2]={'Z'-'@',0}; strcat(pdu,ctrlz); } return communicate(pdu,pdu+MAXPDU,950); } void reset_termios(int f, speed_t baudrate) { struct termios devicetermios, ttytermios; if(tcgetattr(f,&devicetermios)) perror("cannot tcgetattr 1"); memcpy(&devicetermios0,&devicetermios,sizeof(struct termios)); cfsetispeed(&devicetermios,baudrate); cfsetospeed(&devicetermios,baudrate); devicetermios.c_iflag |= IGNBRK; devicetermios.c_iflag &= ~BRKINT; devicetermios.c_iflag &= ~ICRNL; devicetermios.c_iflag &= ~IMAXBEL; devicetermios.c_iflag &= ~IXON; devicetermios.c_iflag &= ~IXOFF; devicetermios.c_oflag &= ~OPOST; devicetermios.c_oflag &= ~ONLCR; devicetermios.c_cflag |= CLOCAL; devicetermios.c_cflag &= ~CRTSCTS; devicetermios.c_lflag &= ~ISIG; devicetermios.c_lflag &= ~ICANON; devicetermios.c_lflag &= ~IEXTEN; devicetermios.c_lflag &= ~ECHO; devicetermios.c_lflag &= ~ECHOE; devicetermios.c_lflag &= ~ECHOK; devicetermios.c_lflag &= ~ECHOCTL; devicetermios.c_lflag &= ~ECHOKE; if(tcsetattr(f,TCSANOW,&devicetermios)) perror("cannot tcsetattr 1"); } int flushing=0; struct sigaction hupaction; void huphandler(int i) { sigaction(SIGHUP,&hupaction,NULL); flushing=1; time_t t; struct tm *tmp; t = time(NULL); tmp = localtime(&t); char timestamp[64]; strftime(timestamp,64,"%c %Z",tmp); fprintf(logfilepointer,"%s: SIGHUP caught\n",timestamp); fflush(logfilepointer); } main(int argc, char **argv) { char fifofile[128]="/usr/local/alarm/smsfifo_out"; char fifofile_out[128]="/usr/local/alarm/smsfifo_in"; #ifdef CRIS char phone_device[64]="/dev/ttyS3"; #else #ifdef G20 char phone_device[64]="/dev/ttyS4"; #else #ifdef RPI char phone_device[64]="/dev/ttyUSB3"; #else char phone_device[64]="/dev/ttyUSB2"; #endif #endif #endif char logfile[256]="/usr/local/alarm/sms.log"; char baud[16]="19200"; speed_t baudrate=B19200; FILE *l; while((--argc>0)&&((*++argv)[0]=='-')){ if (argc==0) usage(); switch ((*argv)[1]){ case 'P': ++argv; --argc; strcpy(phone_device,*argv); break; case 'L': ++argv; --argc; strcpy(logfile,*argv); break; case 'F': ++argv; --argc; strcpy(fifofile,*argv); break; case 'f': ++argv; --argc; strcpy(fifofile_out,*argv); break; case 'B': strcpy(baud,(*argv)+2); if(!strcmp(baud,"2400")) baudrate=B2400; if(!strcmp(baud,"4800")) baudrate=B4800; if(!strcmp(baud,"9600")) baudrate=B9600; if(!strcmp(baud,"19200")) baudrate=B19200; if(!strcmp(baud,"38400")) baudrate=B38400; if(!strcmp(baud,"57600")) baudrate=B57600; if(!strcmp(baud,"115200")) baudrate=B115200; if(!strcmp(baud,"230400")) baudrate=B230400; if(baudrate==B0) xerror("unsupported baudrate"); break; default : xerror("unknown option,; use option -H for information"); } } if(argc !=0) xerror("wrong command line; use option -H for information"); #ifdef CRIS putenv("TZ=CET"); putenv("TZDIR=/usr/local/etc/zoneinfo"); #endif //check if we are already running { FILE *pf=fopen("/usr/local/alarm/smsserver.pid","r"); if(pf) { pid_t oldpid; if(1==fscanf(pf,"%d",&oldpid)) { if(kill(oldpid,0)==0) { fprintf(stderr,"It seems that smsserver is already running.\n Check ps and possibly remove smsserver.pid\n"); exit(10); } } fclose(pf); } } //open logfile l=fopen(logfile,"a"); if(!l) xerror("cannot open logfile"); logfilepointer=l; { time_t t; struct tm *tmp; t = time(NULL); tmp = localtime(&t); char timestamp[64]; strftime(timestamp,64,"%c %Z",tmp); fprintf(l,"\n%s COLD Start SMSserver\n",timestamp); fflush(l); } //this has to be done as root phone_on(1); #ifndef emulate struct passwd *pwd = getpwnam("alarm"); if(chown(phone_device,pwd->pw_uid,pwd->pw_gid)) {perror("cannot chown tty"); exit(10);} if(setgid(pwd->pw_gid)) {perror("cannot setgid to alarm"); exit(10);} if(setuid(pwd->pw_uid)) {perror("cannot setuid to alarm"); exit(10);} #endif if(chdir("/usr/local/alarm")) {perror("cannot chdir to /usr/local/alarm"); exit(10);} if(daemon(1,0)) {perror("cannot become daemon"); exit(10);} //create a pidfile { FILE *pf=fopen("/usr/local/alarm/smsserver.pid","w"); fprintf(pf,"%d\n",getpid()); fclose(pf); } //for debugging { if(chdir("/usr/local/alarm") ) {perror("cannot chdir to /usr/local/alarm"); exit(10);} prctl(PR_SET_DUMPABLE,1); struct rlimit rlim; rlim.rlim_cur = RLIM_INFINITY; rlim.rlim_max = RLIM_INFINITY; setrlimit(RLIMIT_CORE, &rlim); } int f_fifo=open(fifofile, O_RDWR+O_SYNC+O_NOCTTY,0); //actually rdonly but mysterious troubles occur if(f_fifo<0) {fprintf(l,"cannot open connection to fifo\n"); exit(10); } int f_fifo_out=open(fifofile_out, O_RDWR+O_SYNC+O_NOCTTY,0); //actually wronly, but mysterious troubles if(f_fifo_out<0) {fprintf(l,"cannot open connection to fifo out\n"); exit(10); } hupaction.sa_handler = huphandler; hupaction.sa_flags = SA_RESTART; sigaction(SIGHUP,&hupaction,NULL); //////////////////////////// restart: { time_t t; struct tm *tmp; t = time(NULL); tmp = localtime(&t); char timestamp[64]; strftime(timestamp,64,"%c %Z",tmp); fprintf(l,"\n%s HOT Start SMSserver\n",timestamp); fflush(l); } { char cmd[256]; sprintf(cmd,"/bin/stty -F %s clocal",phone_device); int r=system(cmd); if(r) fprintf(stderr,"system returned %d\n",r); } int f=open(phone_device,O_RDWR+O_SYNC+O_NOCTTY+O_EXCL,0); if(f<0) {fprintf(l,"cannot open connection to phone\n"); exit(10); } phone_fd = f; filehandle=f; reset_termios(f,baudrate); phone_reset(); //MAIN LOOP int rr=0; int rr_fifo=0; unsigned int maincounter=0; while(1) { char number_repeat[16]; char text_repeat[MAXSMS]; int sms_repeat=0; int sms_failed=0; sigaction(SIGHUP,&hupaction,NULL); int unsolicited=0; ++maincounter; #ifdef debug fprintf(logfilepointer,"DEBUG Main loop %u\n",maincounter); fflush(logfilepointer); #endif char line[MAXLINE]; char line_fifo[MAXLINE]; //select and read line char *p=line; char *p_fifo=line_fifo; char *x=NULL; char *x_fifo=NULL; int fifo_input=0; int sms_input=0; //accumulate lines from each input source //fprintf(stderr,":"); fd_set readfds; fd_set writefds; fd_set exceptfds; int r,s; FD_ZERO(&writefds); FD_ZERO(&exceptfds); FD_ZERO(&readfds); FD_SET(f,&readfds); FD_SET(f_fifo,&readfds); int maxf=f; if(f_fifo>f) maxf=f_fifo; //timeout the select and poll by CMGL regularly (timeout gets overwritten in select()) struct timeval timeout; timeout.tv_sec=8; timeout.tv_usec=0; int selectfailed=0; if((s=select(maxf+1,&readfds,&writefds,&exceptfds,&timeout)) == -1) { fprintf(logfilepointer,"select(main) return code -1: %s\n",strerror(errno)); fflush(logfilepointer); sleep(1); selectfailed=1; goto endmainloop; } if(s==0) flushing=0; //on timeout return to normal mode if(FD_ISSET(f,&readfds)) //accomodating for fast input from phone { x=NULL; while((p-line) < MAXLINE-1) { r=read(f,p,1); if(r!=1) break; rr+=1; p+=1; if(p[-1] == '\n' || p[-1] == '\r') { x=p-1; break; } } sms_input= x || rr>=MAXLINE-1; } if(FD_ISSET(f_fifo,&readfds)) //take into account fast input from alarmserver { x_fifo=NULL; while((p_fifo-line_fifo) < MAXLINE-1) { r=read(f_fifo,p_fifo,1); if(r!=1) break; rr_fifo+=1; p_fifo+=1; if(p_fifo[-1] == '\n' || p_fifo[-1] == '\r') { x_fifo=p_fifo-1; break; } } fifo_input= x_fifo || rr_fifo>=MAXLINE-1; } char timestamp[64]; { time_t t; struct tm *tmp; t = time(NULL); tmp = localtime(&t); strftime(timestamp,64,"%c %Z",tmp); } unsolicited=0; if(sms_input) { rr=0; x[0]=0; if(line[0]!='^' && line[0]!='\r' && line[0]!=0) fprintf(l,"%s Unsolicited message from phone: %s\n",timestamp,line); ring= strstr(line,"RING")?1:0; //not stray OK, newline or AT+CMGL unsolicited=ring; //other possible unsolicited status messages from huawei modems //if(line[0] == '^') unsolicited=1; //this is just garbage if(ring) //spawn mgetty { int status; char name[256]; strcpy(name,"/usr/local/var/lock/LCK.."); strcat(name,basename(phone_device)); unlink(name); close(filehandle); //close the phone connection pid_t ff=fork(); if(!ff) //child { int r; r=execl("/usr/local/sbin/mgetty","/usr/local/sbin/mgetty",phone_device,(char *)NULL); fprintf(l,"%s cannot execl mgetty %d\n",timestamp,r); fflush(l); exit(1000); //should never get here } else //parent { fprintf(l,"%s spawning mgetty as pid %d\n",timestamp,ff); fflush(l); int setjmp_status=setjmp(env2); if(setjmp_status==0) //normal { struct itimerval itv; memset((void *)&itv,0,sizeof(itv)); itv.it_value.tv_sec = itv.it_interval.tv_sec = 600; //user should be done in 10 minutes itv.it_value.tv_usec = itv.it_interval.tv_usec = 0; if(SIG_ERR == signal(SIGALRM,myjump2)) {fprintf(logfilepointer,"signal 2 failed\n");}; if(0!= setitimer(ITIMER_REAL, &itv, NULL)) {signal(SIGALRM,SIG_IGN); fprintf(logfilepointer,"setitimer 2 failed\n");} waitpid(ff, &status,0); //wait for child exit //once hanged here, timeout did not work!!! } else //from timeout - child hanged { kill(ff,SIGKILL); } } { time_t t; struct tm *tmp; t = time(NULL); tmp = localtime(&t); strftime(timestamp,64,"%c %Z",tmp); } fprintf(l,"%s mgetty exited with status %d, restarting smsserver\n",timestamp,status); fflush(l); phone_on(0); sleep(2); phone_on(1); ring=0; goto restart; } sms_input=0; } if(fifo_input) { rr_fifo=0; x_fifo[0]=0; fprintf(l,"%s Command from FIFO: %s %s\n",timestamp,line_fifo,(flushing?"FLUSHED!":"")); fflush(l); if(!flushing) switch(line_fifo[0]) //processing commands from named pipe { case 'S': //send SMS { char number[16]; char text[MAXSMS]; int r,nread; int shift=2; if(line_fifo[shift] == '+') ++shift; //ignore leading + if(1!=sscanf(line_fifo+shift,"%16s %n",number,&nread)) break; strncpy(text,line_fifo+shift+nread,MAXSMS-1); //spaces will not be a separator number[15]=0; text[MAXSMS-1]=0; r=send_sms(number,text); fprintf(l,"%s SMS SEND %s %s: return code %d\n",timestamp,number,text,r); fflush(l); if(r>0) sms_failed=1; if(r==1) { sms_repeat=5; strcpy(number_repeat,number); strcpy(text_repeat,text); } } break; case 'D': //dial a number { char number[16]; if(1!=sscanf(line_fifo+2,"%16s",number)) break; number[15]=0; fprintf(l,"%s DIALED %s\n",timestamp,number); dial(number); } break; case 'R': //reset phone phone_on(0); sleep(2); phone_on(1); sleep(5); phone_reset(); fprintf(l,"%s Phone cycled and resetted\n",timestamp); break; default:; } } //test for incoming messages quite often or when a RING message came if((maincounter&0x0f) == 0 || ring) { char number[16]; char text[MAXSMS]; char pdu[MAXPDU+1]; int r=read_sms(number,text,1,pdu); text[MAXSMS-1]=0; number[15]=0; if(r==0) { char buf[MAXSMS+16+32]; fprintf(l,"%s INCOMING PDU: %s\n",timestamp,pdu); fflush(l); fprintf(l,"%s INCOMING SMS: %s %s\n",timestamp,number,text); fflush(l); sprintf(buf,"%s %s\n",number,text); int rr=write(f_fifo_out,buf,strlen(buf)); //pass it to alarmserver if(rr!=strlen(buf)) {fprintf(l,"Error of write: %d\n",rr); fflush(l);} //process direct echo for testing if(!strncmp(text,"directecho",10) || !strncmp(text,"DIRECTECHO",10) ) { int rr=send_sms(number,text); fprintf(l,"%s DIRECTECHO send returned %d\n",timestamp,rr); fflush(l); } } } endmainloop: ring=0; //reset the phone approx. monthly if(sms_repeat || sms_failed|| phone_not_on>2 || selectfailed || (maincounter&0x1ffff)==0) { fprintf(l,"%s Switching phone ON\n",timestamp); phone_on(0); sleep(3); phone_on(1); phone_reset(); sms_failed=0; phone_not_on=0; sleep(3); } if(sms_repeat) { sleep(10); int r=send_sms(number_repeat,text_repeat); fprintf(l,"%s REPEATED SMS SEND %s %s: return code %d\n",timestamp,number_repeat,text_repeat,r); fflush(l); if(r==0) sms_repeat=0; else --sms_repeat; } } //MAIN loop fclose(l); close(f); }