help with a little program in linux, need to catch an event - Page 1
Forum section: electronics / beginners

author: coldfiremc
author_rank: Regular Contributor
timestamp: May 14, 2018, 04:07:38 am
content: HiI have to make a C program for Raspbian, which, among other stuff creates a binary file, and fills it with serial port captured stuff everyday. So, no matter the time the machine is turned on, just on midnight, i have to create a new file. I Can't find anything like (I'am a sort of MSDOS guy) INT70h alarm event, that triggers at a specific hour. If the solution is available just with the standard C library, it could be great. Is there anything with that behaviour?thanks

author: saike
author_rank: Regular Contributor
timestamp: May 14, 2018, 04:26:08 am
content: Take a look at crontab exampleshttps://opensource.com/article/17/11/how-use-cron-linuxwhich run tasks at specified times

author: coldfiremc
author_rank: Regular Contributor
timestamp: May 14, 2018, 04:59:54 am
content: ok, I need to specify more details: I need to catch an asynchronous event, with maximum precision available. alarm() looks great, except by one quirk: i need to specify an "explicit" amount of time. If I Can "subtract" ticks between the current time and midnight on the fly would be great. I was about to use date, but delays can occur, and also, I have to "unnecesarily" parse the output.

author: hamster_nz
author_rank: Super Contributor
timestamp: May 14, 2018, 05:14:12 am
content: You could use an alarm to signal to the program to see if it needs to open a new file, or you could use something like this, to read the serial port and open a new file when the first input is seen on a new day.Code:#include <stdio.h>#include <time.h>#include <sys/time.h>int main(int argc, char *argv[]){FILE *f = NULL;FILE *fin = NULL;int year = 0,month = 0,day = 0;int c;/* Select or open the input file */if(argc == 1) {fin = stdin;} else if(argc == 2) {fin = fopen(argv,"rb");if(fin == NULL) {fprintf(stderr,"Unable to open '%s'\n",argv);} else {fprintf(stderr,"Opened '%s'\n",argv);}} else {fprintf(stderr,"Usage : %s [input file]\n",argv);}/* Process the characters */while(fin != NULL) {time_t now_secs;struct tm *now;/* Get a character */c = getc(fin);if(c == EOF) break;/* Get the time */now_secs = time(NULL);now = localtime(&now_secs);/* Do we need to open a new file */if(f == NULL|| now->tm_mday != day|| now->tm_mon != month|| now->tm_year != year) {char buffer;year = now->tm_year;month = now->tm_mon;day = now->tm_mday;/* Close any existing file */if(f != NULL) {fclose(f);f = NULL;}/* Make a file name and open it - check the length of buffer if you change this! */sprintf(buffer,"log.%04i-%02i-%02i",year+1900,month,day);f = fopen(buffer,"a");if(f == NULL) {fprintf(stderr,"Unable to open '%s'\b", buffer);break;}}/* Output the character to the file, flush if a newline is seen */if( f != NULL) {putc(c,f);if(c == '\n')fflush(f);}}/* Cleanup */if(argc == 1 && fin != NULL) {fclose(fin);}if(f != NULL)fclose(f);return 0;}If this meets your requirements or not depends if you need an empty file on a day where you have no input. You could change the 'getc()' call to 'select()' with a timeout of a few seconds, which would allow you to avoid the hassles of managing signals. That would involve switching to the unbuffered open()/read()/close() to process the input file, which is just a bit awkward for a quick hack.You should either 'nohup' it, or set it up as a service (e.g. as script in /etc/init.d or add it to /etc/rc.local or maybe in /etc/inittab) so that it runs on boot, and/or restarts if it gets killed by a user.If you need to configure the serial port (e.g. set its speed, flow control and so on) either use the 'stty' utility before you open it, or have a read of the man page for 'tcgetattr' and add the required calls to the program.

author: hamster_nz
author_rank: Super Contributor
timestamp: May 14, 2018, 05:22:52 am
content: Quote from: coldfiremc on May 14, 2018, 04:07:38 amIf you really do want to set an alarm, this will help:http://www.gnu.org/software/libc/manual/html_node/Setting-an-Alarm.htmlYou will need to set up an empty signal handler for SIGALRM, as the default behavior is to terminate the process.I think you will find that read() or getc() will return -1 with errno set to "EINTR" when the alarm is triggered, avoiding the need to do any real work in your interrupt handler.Code:#include <stdio.h>#include <signal.h>#include <unistd.h>#include <errno.h>void handler(int i) {/* Do nothing in the signal handler */}int main(int argc, char *argv[]) {int carry_on = 1;struct sigaction sa;sa.sa_handler = handler;sa.sa_flags = 0;sigfillset(&sa.sa_mask);if(sigaction(SIGALRM, &sa, NULL) == -1) {perror("Error: cannot handle SIGALRM");}while(carry_on) {int c;/* Set up an alarm in two seconds */alarm(2);/* read a character */c = getchar();if(c == EOF) {/* Trap the alarm */switch(errno) {case EINTR:printf("EINTR - alarm called\n");break;default:carry_on = 0;break;}} else {/* Output the character */putchar(c);}}return 0;}

author: Nominal Animal
author_rank: Super Contributor
timestamp: May 14, 2018, 07:15:41 am
content: The clock in Linux runs in UTC, so you can useclock_gettime(CLOCK_REALTIME, &ts)to obtain the current UTC time at nanosecond resolution (but precision is obviously less). You can usenanosleep(&desired, &left)to sleep for short durations (although the sleep duration is often slightly longer than requested). Ifnanosleep()is interrupted due to e.g. the delivery of a signal, the second timespec structure will be filled in with the unslept duration left from the original desired duration.Reacting to events at high time precision is simple: you ensure you have a thread or threads running at high enough priority, preferably using realtime scheduler (SCHED_FIFOorSCHED_RR); this requires either root privileges, or theCAP_SYS_NICEcapability that you can give to a binary executable using e.g.setcap CAP_SYS_NICE=pe /path/to/executableif you use ext4/3/2, btrfs, xfs, tmpfs, or other Unixy filesystem; will not work on VFAT or ntfs), and have them block at the event source(s) usingread(),select(),poll()or similar.For calculating the difference in seconds (using double-precision floating point) between two timespecs, I useCode:static inline double timespec_diff(const timespec *const after, const timespec *const before){return (double)(after->tv_sec - before->tv_sec)+ (double)(after->tv_nsec - before->tv_nsec) / 1000000000.0; /* 1.0 s = 1 000 000 000 . 0 ns */}It works exactly like standard Cdifftime(). Ifafteris beforebefore, the result is negative.These (and all POSIX.1-2008 features) are baked in to the Linux C library. All you need to do is toCode:#define _POSIX_C_SOURCE 200809Lbefore any#includes, so that the C library headers will expose the features for you. You can also addCode:#define _GNU_SOURCE#define _BSD_SOURCEto expose all GNU and BSD features built in to the C library, respectively. There are some nice nuggets in there.As to the overall problem, you haven't mentioned anything actually pertinent -- like how do the events occur, how your program is supposed to detect them, and what kind of actions should be performed in addition to logging them.Right now, I'm working with a very finicky serial interface to a high-voltage power supply (100 kV, at up to 50 mA). To make the "driver" (daemon, really, in Unix/POSIX parlance; TSR in DOS) robust enough, I fortunately have the help of a colleague to decipher the functional states of the device. For stuff like this, knowing the details before writing any code means the difference between less than satisfactory and satisfactory end results. (This is not user-facing stuff, so things like agile design methodology does not apply; this stuff needs to be properly designed and engineered, to work properly and reliably.)

author: coldfiremc
author_rank: Regular Contributor
timestamp: May 22, 2018, 10:00:13 pm
content: Hi, I'm getting some things to work, Now i'm able to get time, make a string with time and use it as filename, close and create files automagically, but i have problems "patching" the current day, with the midnight time.I want to do difftime() between some time (day start, or program start) against the next midnight. I decided to get a date and patch the struct to then use mktime to build a new time_t date... but fails complaining type incompatibility. I can understand pointers at low level, but C notation confuses me a bit so i'm sure that my error is there.Code:....//header declarationsstatic void midnight_handler(int signo);void create_name();//char *file_name;time_t curtime;int count;time_t midnight;struct tm *info;char strbuffer ;int newday;void init_midnight(){time(&midnight);midn=localtime(&midnight);midn.tm_sec=59;midn.tm_hour=23;midnight=mktime(&midn); //errors here!}thanks

author: Nominal Animal
author_rank: Super Contributor
timestamp: May 22, 2018, 11:15:06 pm
content: Maybe the following example program helps?Code:#define _POSIX_C_SOURCE 200809L#include <stdlib.h>#include <time.h>#include <errno.h>#include <stdio.h>static int timeofday(struct timespec *t, const int hour, const int min, const int sec, const long nanosec){struct tm ltime;time_t newt;if (!t)return -1;if (!localtime_r(&(t->tv_sec), &ltime))return -1;ltime.tm_hour = hour;ltime.tm_min = min;ltime.tm_sec = sec;ltime.tm_isdst = -1; /* Don't know if DST, check! */newt = mktime(&ltime);if (newt == (time_t)-1)return -1;t->tv_sec = newt;t->tv_nsec = nanosec; /* 1000000000 ns = 1 second */return 0;}static const char *localtimestamp(char *buf, size_t len, const struct timespec *t){struct tm ltime;int n;if (!buf || len < 1 || !t)return NULL;if (!localtime_r(&(t->tv_sec), &ltime))return NULL;n = snprintf(buf, len, "%04d%02d%02dT%02d:%02d:%02d.%03d",ltime.tm_year+1900, ltime.tm_mon+1, ltime.tm_mday,ltime.tm_hour, ltime.tm_min, ltime.tm_sec, (int)(t->tv_nsec / 1000000L));if (n < 1 || (size_t)n >= len - 1)return NULL;return buf;}int main(void){char timestamp;struct timespec now, then;clock_gettime(CLOCK_REALTIME, &now);printf("Now is %s in local time.\n", localtimestamp(timestamp, sizeof timestamp, &now));then = now;timeofday(&then, 12, 0, 0, 0L);printf("Noon today: %s.\n", localtimestamp(timestamp, sizeof timestamp, &then));then = now;timeofday(&then, 0, 0, 0, 0L);printf("Last midnight: %s.\n", localtimestamp(timestamp, sizeof timestamp, &then));then = now;timeofday(&then, 23, 59, 60, 0L);printf("Next midnight: %s.\n", localtimestamp(timestamp, sizeof timestamp, &then));return EXIT_SUCCESS;}Thelocaltime_r(&time, &timespec)converts atime_tor seconds since epoch, to astruct timespec. It returnsNULLif it fails; it does not normally fail. Unlikelocaltime(),localtime_r()uses thestruct timespecit is given a pointer at, and is thread-safe.It is perfectly okay to exceed thetm_fields, asmktime()will normalize them. For example, if you settm_hour=36, you'll get the seconds since epoch corresponding to midday on the following day. Note that thetm_fields themselves are also normalized, so you often see a call likemktime(&ltime);where the result is discarded. This call simply normalizes the fields inltimeto their proper values, allowing very easy way to calculate split datesNminutes, hours, days, or months away.For example:Code:#define _POSIX_C_SOURCE 200809L#include <stdlib.h>#include <string.h>#include <stdio.h>#include <time.h>int main(int argc, char *argv[]){char units;time_t nowtime;struct tm now;int arg, value;char dummy;nowtime = time(NULL);if (!localtime_r(&nowtime, &now)) {fprintf(stderr, "The C library does not support current time.\n");return EXIT_FAILURE;}printf("Now: %04d-%02d-%02d %02d:%02d:%02d\n",now.tm_year+1900, now.tm_mon+1, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec);for (arg = 1; arg < argc; arg++)if (sscanf(argv, " %d %9s %c", &value, units, &dummy) == 2) {if (!strcmp(units, "y") || !strcmp(units, "a") || !strcmp(units, "year") || !strcmp(units, "years"))now.tm_year += value;elseif (!strcmp(units, "mo") || !strcmp(units, "mos") || !strcmp(units, "month") || !strcmp(units, "months"))now.tm_mon += value;elseif (!strcmp(units, "d") || !strcmp(units, "day") || !strcmp(units, "days"))now.tm_mday += value;elseif (!strcmp(units, "h") || !strcmp(units, "hrs") || !strcmp(units, "hour") || !strcmp(units, "hours"))now.tm_hour += value;elseif (!strcmp(units, "m") || !strcmp(units, "min") || !strcmp(units, "mins") || !strcmp(units, "minute") || !strcmp(units, "minutes"))now.tm_min += value;elseif (!strcmp(units, "s") || !strcmp(units, "sec") || !strcmp(units, "second") || !strcmp(units, "seconds"))now.tm_sec += value;else {fprintf(stderr, "%s: Unknown time units '%s'.\n", argv, units);return EXIT_FAILURE;}} else {fprintf(stderr, "%s: Cannot understand relative time definition.\n", argv);return EXIT_FAILURE;}now.tm_isdst = -1; /* We do not know if DST or not. */mktime(&now);printf("Then: %04d-%02d-%02d %02d:%02d:%02d\n",now.tm_year+1900, now.tm_mon+1, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec);return EXIT_SUCCESS;}if you compile the above as./ex2(say,gcc -Wall -O2 ex2.c -o ex2), then for example./ex2 '-1 year' +5days '-1 month' "+223 hours" "-450 minutes"will output something likeNow: 2018-05-23 02:11:35Then: 2017-05-07 01:41:35demonstrating how you can manipulate thetm_fields, and havemktime()normalize them. (Within limits of the Unix Epoch time, of course.)As you can see, with POSIX tools manipulating date and time expressions is really quite easy.

author: dmills
author_rank: Super Contributor
timestamp: May 23, 2018, 03:19:39 pm
content: If this is just logging, you may be better to just use the syslog facility and configure syslogd and logrotate to do the right thing for your particular log file.These handle automatically timestamping, and splitting log files by size or time threshold, and logrotate will even zip up old logs and (programmably) delete very old ones.These are standard system tools on most linux installs.Remember also that Posix file systems are reference counted so files are only deleted when the last user closes them, this can be a source of 'fun' is you have some long running program writing to a very large file that you wish to delete as it will not really go away until the long running program closes the file.Another approach is to use the timeout in the select system call which will return either when a timeout expires or data is available, use select on the serial port fd and set a timeout for however many seconds to midnight remain, then do the appropriate thing when select returns before resetting the timeout and going again.Regards, Dan.

author: Nominal Animal
author_rank: Super Contributor
timestamp: May 23, 2018, 04:50:09 pm
content: Quote from: dmills on May 23, 2018, 03:19:39 pmFully agreed. (From the question, I assumed this was not the case.)openlog()/syslog()/closelog()is always preferable to logging to custom files, because syslog is easy to configure to match the administrator needs, including logging to remote machines. Logrotate can rotate logs at midnight without any effort or notice by the application producing the log.

author: bd139
author_rank: Super Contributor
timestamp: May 23, 2018, 05:17:55 pm
content: Definitely syslog.Always approach Unix with two points in mind:1. What is there that already does this and can I use it?2. How can I write something with the smallest surface area and leverage what I found that was close in (1).Some of the best Unix programs, even solving domains like this are one liners in the shell.