forked threads in C are writing each other stdout - c
I wrote a little daemon.
This is the flow of the daemon, in general:
Get variables
Get all rows from the database (may be either MySQL or Oracle) that meets the query parameters (in this case get all rows that has this current time).
If any rows were found then run a Perl script for each row (using execv).
This daemon works well, but the problem is that when I have two rows or more coming back from the query, they start, but the Perl script outputs are mixed. They are supposed to run independently without interfering with each other.
Am I doing something wrong?
Is this a memory leak?
Here is my code :
/*
============================================================================
Name : main.c
Description : Daemon for scheduler
============================================================================
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <strings.h>
#include <regex.h>
#include <time.h>
#ifdef MYSQL_CODE
#include <mysql.h>
#endif
#ifdef ORACLE_CODE
#include <ocilib.h>
#endif
#define DAEMON_NAME "schedulerd"
void start_job(char *automation_user,char *automation_path,char *automation_log_path, char *id, char *site, char *db_type,char *db_host,char *db_user,char *db_password,char *db_schemata){
pid_t pid;
pid = fork();
if (pid < 0) { exit(EXIT_FAILURE); }
//We got a good pid, Continue to the next result
if (pid > 0) {
return;
}
char *file_format = "scheduler";
char *seperator = "_";
char *postfix = "_XXXXXX";
char *extension = ".log";
//get Time
time_t now;
struct tm *now_tm;
char hour[2],min[2],sec[2];
now = time(NULL);
now_tm = localtime(&now);
sprintf(hour, "%d", now_tm->tm_hour);
sprintf(min, "%d", now_tm->tm_min);
sprintf(sec, "%d", now_tm->tm_sec);
char *str = (char *)automation_log_path;
strcat(str,(char *)file_format);
strcat(str,seperator);
strcat(str,hour);
strcat(str,seperator);
strcat(str,min);
strcat(str,seperator);
strcat(str,sec);
strcat(str,postfix);
// buffer to hold the temporary file name
char nameBuff[128];
int filedes = -1;
// memset the buffers to 0
memset(nameBuff,0,sizeof(nameBuff));
// Copy the relevant information in the buffers
strncpy(nameBuff,str,128);
// Create the temporary file, this function will replace the 'X's
filedes = mkstemp(nameBuff);
if(filedes<1)
{
syslog (LOG_NOTICE, "Creation of temp file [%s] failed with error [%s]\n",nameBuff,strerror(errno));
}else{
mode_t perm = 0644;
fchmod(filedes, perm); //Change permission to the file so everyone can read
close(filedes); // Close created file
//Rename file
int ret;
char newname[128];
sprintf(newname, "%s%s", nameBuff,extension);
ret = rename(nameBuff, newname);
if(ret != 0) {
syslog (LOG_NOTICE, "Renaming of temp file %s to %s failed \n",nameBuff,newname);
exit(EXIT_FAILURE);
}
char statement[256];
sprintf(statement, "UPDATE scheduler_list SET log_file='%s' WHERE id='%s'", newname,id);
syslog (LOG_NOTICE,"Adding to DB : %s\n", statement);
char command[2048];
sprintf(command, "cd %s ; ./runner.pl -site %s -log %s -scheduler_id %s -command \\\"./run_me.pl\\\"", automation_path,site,newname,id);
//sprintf(command, "cd /net/10.7.5.50/opt/trunk/ ; ./runner.pl -site %s -log %s -scheduler_id %s -command \\\"./run_me.pl\\\"",site,newname,id);
if (strcasestr(db_type,"mysql")){/* mysql */
#ifdef MYSQL_CODE
MYSQL *conn;
//mysql_free_result(res); // Free mysql
conn = mysql_init(NULL);
/* Connect to database */
if (!mysql_real_connect(conn, db_host,db_user, db_password, db_schemata, 0, NULL, 0)) {
syslog (LOG_NOTICE,"%s\n", mysql_error(conn));
exit(EXIT_FAILURE);
}
if (mysql_query(conn,statement)) {
syslog (LOG_NOTICE,"%s\n", mysql_error(conn));
exit(EXIT_FAILURE);
}
#endif
}else{
#ifdef ORACLE_CODE
OCI_Connection* cn;
OCI_Statement* st;
OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT);
char query_command[128];
sprintf(query_command, "%s:1521/%s", db_host,db_schemata);
cn = OCI_ConnectionCreate(query_command, db_user, db_password, OCI_SESSION_DEFAULT);
st = OCI_StatementCreate(cn);
OCI_Prepare(st, statement);
OCI_Execute(st);
OCI_Commit(cn);
OCI_Cleanup();
#endif
}
char *args[] = {"sudo", "-u", automation_user, "bash","-c",command,NULL};
FILE *log_file_h = fopen(newname, "w");
if (log_file_h == NULL)
{
syslog (LOG_NOTICE,"Error opening file %s !\n",newname);
exit(EXIT_FAILURE);
}
syslog (LOG_NOTICE,"Starting scheduler job %s , command : %s",id, command);
fclose(log_file_h);
execv("/usr/bin/sudo",args);
syslog (LOG_NOTICE,"Failed to start job %s ",id);
perror("error");
}
exit(EXIT_FAILURE);
}
void process(char *automation_user,char *automation_path, char *automation_log_path ,char *db_type,char *db_host,char *db_user,char *db_password,char *db_schemata){
if (strcasestr(db_type,"mysql")){/* mysql */
#ifdef MYSQL_CODE
MYSQL *conn;
MYSQL_RES *res;
MYSQL_ROW row;
conn = mysql_init(NULL);
/* Connect to database */
if (!mysql_real_connect(conn, db_host,db_user, db_password, db_schemata, 0, NULL, 0)) {
syslog (LOG_NOTICE,"%s\n", mysql_error(conn));
return;
}
/* send SQL query */
if (mysql_query(conn, "SELECT id,site from scheduler_list where start_date = DATE_FORMAT(now(),'%Y-%m-%d %k:%i:00') AND id != father_id AND run='yes'")) {
syslog (LOG_NOTICE,"%s\n", mysql_error(conn));
return;
}
res = mysql_use_result(conn);
/* output table name */
while ((row = mysql_fetch_row(res)) != NULL){
char *id = malloc(strlen(row[0]) +1);
strcpy(id,row[0]);
char *site = malloc(strlen(row[1]) +1);
strcpy(site,row[1]);
start_job(automation_user,automation_path,automation_log_path,id,site,db_type, db_host,db_user,db_password,db_schemata);
}
/* close connection */
mysql_free_result(res);
mysql_close(conn);
#endif
}else{/* oracle */
#ifdef ORACLE_CODE
OCI_Connection* cn;
OCI_Statement* st;
OCI_Resultset* rs;
OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT);
char query_command[128];
sprintf(query_command, "%s:1521/%s", db_host,db_schemata);
cn = OCI_ConnectionCreate(query_command, db_user, db_password, OCI_SESSION_DEFAULT);
st = OCI_StatementCreate(cn);
OCI_ExecuteStmt(st, "SELECT id,site from scheduler_list where to_char(start_date, 'yyyy-mm-dd hh24:mi') = to_char(SYSDATE, 'yyyy-mm-dd hh24:mi') AND id != father_id AND run='yes'");
rs = OCI_GetResultset(st);
while (OCI_FetchNext(rs)){
char *id = malloc(strlen(OCI_GetString(rs, 1)) +1);
strcpy(id,OCI_GetString(rs,1));
char *site = malloc(strlen(OCI_GetString(rs,2)) +1);
strcpy(site,OCI_GetString(rs,2));
start_job(automation_user,automation_path,automation_log_path,id,site,db_type, db_host,db_user,db_password,db_schemata);
}
OCI_Cleanup();
#endif
}
}
char * set_conf_param (char *line, int addSlash){
char *param = malloc(strlen(line) + 2 + addSlash);
strcpy(param,line);
param = strchr(line,'=');
param = param+1; //Remove '='
strtok(param, "\n"); //remove /n
if (addSlash == 1){
int len = strlen(param);
param[len] = '/';
param[len+1] = '\0';
}
return strdup(param);
}
int main(int argc, char *argv[]) {
FILE * fp;
char * line = NULL;
size_t len = 0;
int found_db = 0;
ssize_t read;
pid_t pid, sid;
char *automation_user=NULL,*automation_log_path=NULL ,*db_type=NULL, *db_host=NULL , *db_user=NULL, *db_password=NULL, *db_schemata=NULL;
char *automation_path = getenv("AUTOMATION_PATH");
//char *automation_path = "/net/10.7.5.50/opt/trunk/";
char *automation_user_search = "automation_user=";
char *automation_log_path_search = "automation_log=";
char *db_type_search = "type=";
char *db_host_search = "host=";
char *db_user_search = "user=";
char *db_password_search = "password=";
char *db_schemata_search = "schemata=";
const char comment = '#';
/* Change the working directory to the root directory */
/* or another appropriated directory */
chdir(automation_path);
//Set our Logging Mask and open the Log
setlogmask(LOG_UPTO(LOG_NOTICE));
openlog(DAEMON_NAME, LOG_CONS | LOG_NDELAY | LOG_PERROR | LOG_PID, LOG_USER);
syslog(LOG_NOTICE, "Entering Daemon");
//Read framework.conf
fp = fopen("framework.conf", "r");
if (fp == NULL){
syslog (LOG_NOTICE,"Failed to open framework.conf");
exit(1);
}
//Read framework.conf
fp = fopen("framework.conf", "r");
if (fp == NULL){
syslog (LOG_NOTICE,"Failed to open framework.conf");
exit(1);
}
while ((read = getline(&line, &len, fp)) != -1) {
//If line commented
if (strchr(line,comment) != NULL){
continue;
}
if (strstr(line,automation_user_search) != NULL){
automation_user = set_conf_param(line,0);
}
else if (strstr(line,automation_log_path_search) != NULL){
automation_log_path = set_conf_param(line,1);
}
else if (db_type!=NULL && strcasestr(line,db_type) != NULL){
found_db = 1;
}
else if (strstr(line,db_type_search) != NULL){
db_type = set_conf_param(line,0);
}
else if (found_db && db_host==NULL && strstr(line,db_host_search) != NULL){
db_host = set_conf_param(line,0);
}
else if (found_db && db_user==NULL && strstr(line,db_user_search) != NULL){
db_user = set_conf_param(line,0);
}
else if (found_db && db_password==NULL && strstr(line,db_password_search) != NULL){
db_password = set_conf_param(line,0);
}
else if (found_db && db_schemata==NULL && strstr(line,db_schemata_search) != NULL){
db_schemata = set_conf_param(line,0);
}
}
fclose(fp);
if (line)
free(line);
if (automation_user==NULL){
automation_user = "root";
}
//Fork the Parent Process
pid = fork();
if (pid < 0) { exit(EXIT_FAILURE); }
//We got a good pid, Close the Parent Process
if (pid > 0) { exit(EXIT_SUCCESS); }
//Change File Mask
umask(0);
//Create a new Signature Id for our child
sid = setsid();
if (sid < 0) { exit(EXIT_FAILURE); }
//Close Standard File Descriptors
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
//----------------
//Main Process
//----------------
while(1){
process(automation_user,automation_path,automation_log_path,db_type,db_host,db_user,db_password,db_schemata); //Run our Process
sleep(60); //Sleep for 60 seconds
}
//Close the log
closelog ();
exit(EXIT_FAILURE);
}
You don't appear to have made any effort to write a minimal, complete verifiable example.
This is just a dump of your whole program, including dependencies on a config file you don't provide, and databases whose schemata you don't show. None of which are even relevant to your question.
At least you edited out the company name, but for reference it's still visible in the edit history.
threads in C are writing each other stdout
I can't see any threads in your program, and you don't fork threads anyway - what you have are child processes
... are writing each other stdout
well, stdout is inherited from the parent, so all children (and their sudo/bash/perl/whatever children) are writing to the same place. The writes themselves are locked, but can be interleaved.
If you want their output not to be interleaved, have them write to different places, and figure out how to display/combine them later.
Per-child-process temporary files are a popular choice, but note the parent will have to track child process completion in order to know when to print and then delete each file. Also, you should probably detach child processes from the terminal, close stdin, and consider what to do with stderr.
Is this a memory leak?
No.
I guess (after a quick glimpse) that the problem is in
execv("/usr/bin/sudo",args);
you should make sure that only one instance is running.
this can be done by mutual exclusion.
A good description is on:
http://www.thegeekstuff.com/2012/05/c-mutex-examples/
In short:
// create a global mutex variable
pthread_mutex_t lock;
// initialise it in main
pthread_mutex_init(&lock, NULL);
// enclose your critical path
pthread_mutex_lock(&lock);
execv("/usr/bin/sudo",args);
pthread_mutex_unlock(&lock);
Hope it Helps
Related
Why not able to find second new event using inotifyd in c language
I am trying check new event created or deleted in the specific directory. If event is created then need to do some operation using thread and this thread should be in initiate wile loop . Main thread should look for another event creation or deletion. I have developed application , but I could not able to second event is created or not. Code flow is always in child thread. Please find below code : #include <errno.h> #include <poll.h> #include <stdio.h> #include <stdlib.h> #include <sys/inotify.h> #include <unistd.h> #include <string.h> #define PATH_MAX 4096 #define O_RDONLY 00 int main() { //char buf; char buf[4096] __attribute__ ((aligned(__alignof__(struct inotify_event)))); int fd, i, poll_num; int *wd; nfds_t nfds; struct pollfd fds[0]; char *device_path = "/home/home/test"; ssize_t len; const struct inotify_event *event; int counter=0; char getdata[10]; char devname[PATH_MAX]; char *filename; int thread_ret = 0; static pthread_t inotify_pthread; /* Create the file descriptor for accessing the inotify API */ fd = inotify_init1(IN_NONBLOCK); printf("fd = %d",fd); if (fd == -1) { perror("inotify_init1"); exit(EXIT_FAILURE); } /* Mark directories for events - file was opened - file was closed */ wd = inotify_add_watch(fd, device_path, IN_OPEN | IN_CLOSE | IN_CREATE | IN_DELETE); printf("wd = %d ",wd); if (wd == -1) { fprintf(stderr, "Cannot watch '%s': %s\n", device_path, strerror(errno)); exit(EXIT_FAILURE); } /* Prepare for polling */ nfds = 1; /* Inotify input */ fds[0].fd = fd; fds[0].events = POLLIN; /* Wait for events and/or terminal input */ printf("Listening for events.\n"); while (1) { poll_num = poll(fds, nfds, -1); if (poll_num == -1) { if (errno == EINTR) continue; perror("poll"); exit(EXIT_FAILURE); } if (poll_num > 0) { if (fds[0].revents & POLLIN) { /* Inotify events are available */ len = read(fd, buf, sizeof(buf)); if (len == -1 && errno != EAGAIN) { perror("read"); exit(EXIT_FAILURE); } if (len <= 0) break; for (char *ptr = buf; ptr < buf + len; ptr += sizeof(struct inotify_event) + event->len) { event = (struct inotify_event *) ptr; if ( event->len ) { /* Print event type */ if (event->mask & IN_CREATE){ printf("IN_CREATE: \n"); if (event->len){ counter++; printf("IN_CREATE: created file name : %s , counter no = %d \n", event->name,counter); thread_ret = pthread_create(&inotify_pthread, NULL, event_read_data(),NULL); if (thread_ret < 0) { printf("can't create inotify thread"); exit(EXIT_FAILURE); } } } else if (event->mask & IN_DELETE){ counter--; printf( "IN_DELETE : Deleted File name %s , counter no = %d \n", event->name,counter ); } } printf("counter number : %d \n",counter); } } } } printf("Listening for events stopped.\n"); close(fd); free(wd); exit(EXIT_SUCCESS); } event_read_data(){ while(1){ printf("Child Thread"); sleep(3); } } Compiled like below: gcc fine_name.c -lpthread
Implementing input/output redirection in a Linux shell using C
I am trying to create a basic shell for Linux using C. I have gotten it to work until I try to do output redirection and it just destroys everything. When I run this code, it goes straight to the default case of the fork(). I have no idea why. If I get rid of the for loop in the child process it works, but even with the for loop I don't understand why the child process is never even entered. If I put a print statement at the top of the child process it doesn't get printed. When I run this in command line, I get the prompt and type something like "ls", which worked before I added the for loop, but now I just get "% am i here" and if I press enter it just keeps giving me that same line. My goal is to be able to type "ls > output" and have it work. I think the input redirection works, but honestly I haven't even played with it much because I am so utterly confused as to what is going on with the output redirection. Any help would be greatly appreciated, I've spent 4 hours on the same like 15 lines trying to get this to work. #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <signal.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/types.h> #include <sys/wait.h> #define HISTORY_COUNT 20 char *prompt = "% "; int main() { int pid; //int child_pid; char line[81]; char *token; char *separator = " \t\n"; char **args; char **args2; char **args3; char cmd[100]; char *hp; char *cp; char *ifile; char *ofile; int check; int pfds[2]; int i; int j; int current = 0; int p = 0; //int check; char *hist[HISTORY_COUNT]; //char history[90]; //typedef void (*sighandler_t) (int); args = malloc(80 * sizeof(char *)); args2 = malloc(80 * sizeof(char *)); signal(SIGINT, SIG_IGN); while (1) { fprintf(stderr, "%s", prompt); fflush(stderr); if (fgets(line, 80, stdin) == NULL) break; // split up the line i = 0; while (1) { token = strtok((i == 0) ? line : NULL, separator); if (token == NULL) break; args[i++] = token; /* build command array */ } args[i] = NULL; if (i == 0){ continue; } // assume no redirections ofile = NULL; ifile = NULL; // split off the redirections j = 0; i = 0; while (1) { //stackoverflow.com/questions/35569673 cp = args[i++]; if (cp == NULL) break; switch (*cp) { case '<': if (cp[1] == 0) cp = args[i++]; else ++cp; ifile = cp; break; case '>': if (cp[1] == 0) cp = args[i++]; else ++cp; ofile = cp; break; case '|': if(cp[1] ==0){ cp = args[i++]; if(pipe(pfds) == -1){ perror("Broken Pipe"); exit(1); } p = 1; } else{ ++cp; } break; default: args2[j++] = cp; args3[cp++] = cp break; } } args2[j] = NULL; if (j == 0) continue; switch (pid = fork()) { case 0: // open stdin if (ifile != NULL) { int fd = open(ifile, O_RDONLY); if (dup2(fd, STDIN_FILENO) == -1) { fprintf(stderr, "dup2 failed"); } close(fd); } // open stdout if (ofile != NULL) { // args[1] = NULL; int fd2; if ((fd2 = open(ofile, O_WRONLY | O_CREAT, 0644)) < 0) { perror("couldn't open output file."); exit(0); } // args+=2; printf("okay"); dup2(fd2, STDOUT_FILENO); close(fd2); } if(p == 1){ //from stackoverflow.com/questions/2784500 close(1); dup(pfds[1]); close(pfds[0]); execvp(args2[0], args2); break; } if(strcmp(args2[0], "cd") == 0){ //cd command if(args2[1] == NULL){ fprintf(stderr, "Expected argument"); } else{ check = chdir(args2[1]); if(check != 0){ fprintf(stderr,"%s",prompt); } } break; } execvp(args2[0], args2); /* child */ signal(SIGINT, SIG_DFL); fprintf(stderr, "ERROR %s no such program\n", line); exit(1); break; case -1: /* unlikely but possible if hit a limit */ fprintf(stderr, "ERROR can't create child process!\n"); break; default: //printf("am I here"); if(p==1){ close(0); dup(pfds[0]); close(pfds[1]); //execvp(); } wait(NULL); //waitpid(pid, 0, 0); } } exit(0); }
I added a separate argument pass to capture and remember the I/O redirections and remove them from the arg list passed to the child. Here's the corrected code [please pardon the gratuitous style cleanup]: #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <signal.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/types.h> #include <sys/wait.h> char *prompt = "% "; int main() { int pid; //int child_pid; char line[81]; char *token; char *separator = " \t\n"; char **args; char **args2; char *cp; char *ifile; char *ofile; int i; int j; int err; //int check; //char history[90]; //typedef void (*sighandler_t) (int); args = malloc(80 * sizeof(char *)); args2 = malloc(80 * sizeof(char *)); //signal(SIGINT, SIG_IGN); while (1) { fprintf(stderr, "%s", prompt); fflush(stderr); if (fgets(line, 80, stdin) == NULL) break; // split up the line i = 0; while (1) { token = strtok((i == 0) ? line : NULL, separator); if (token == NULL) break; args[i++] = token; /* build command array */ } args[i] = NULL; if (i == 0) continue; // assume no redirections ofile = NULL; ifile = NULL; // split off the redirections j = 0; i = 0; err = 0; while (1) { cp = args[i++]; if (cp == NULL) break; switch (*cp) { case '<': if (cp[1] == 0) cp = args[i++]; else ++cp; ifile = cp; if (cp == NULL) err = 1; else if (cp[0] == 0) err = 1; break; case '>': if (cp[1] == 0) cp = args[i++]; else ++cp; ofile = cp; if (cp == NULL) err = 1; else if (cp[0] == 0) err = 1; break; default: args2[j++] = cp; break; } } args2[j] = NULL; // we got something like "cat <" if (err) continue; // no child arguments if (j == 0) continue; switch (pid = fork()) { case 0: // open stdin if (ifile != NULL) { int fd = open(ifile, O_RDONLY); if (dup2(fd, STDIN_FILENO) == -1) { fprintf(stderr, "dup2 failed"); } close(fd); } // trying to get this to work // NOTE: now it works :-) // open stdout if (ofile != NULL) { // args[1] = NULL; int fd2; //printf("PLEASE WORK"); if ((fd2 = open(ofile, O_WRONLY | O_CREAT, 0644)) < 0) { perror("couldn't open output file."); exit(0); } // args+=2; printf("okay"); dup2(fd2, STDOUT_FILENO); close(fd2); } execvp(args2[0], args2); /* child */ signal(SIGINT, SIG_DFL); fprintf(stderr, "ERROR %s no such program\n", line); exit(1); break; case -1: /* unlikely but possible if hit a limit */ fprintf(stderr, "ERROR can't create child process!\n"); break; default: //printf("am I here"); wait(NULL); //waitpid(pid, 0, 0); } } exit(0); } UPDATE: If you're still around do you think you could help me with creating a pipe? Sure. It's too big to post here. See: http://pastebin.com/Ny1w6pUh Wow did you create all 3300 lines? Yes. I borrowed xstr from another SO answer of mine [with bugfix and enhancement]. The dlk was new, but I do many of those, so was easy. Most of it was new code. But ... It was composed of fragments/concepts I've done before: tgb, FWD, BTV, sysmagic. Notice all struct members for struct foo are prefixed with foo_ [standard for me]. The macro "trickery" with DLHDEF and DLKDEF to simulate inheritance/templates is also something I do [when necessary]. Many function vars reuse my style: idx for index var [I would never use i/j, but rather xidx/yidx], cp for char pointer, cnt for count, len for byte length, etc. Thus, I don't have to "think" about small stuff [tactics] and can focus on strategy. The above idx et. al. is a "signature style" for me. It's not necessarily better [or worse] than others. It comes from the fact that I started using C when the linker/loader could only handle 8 character symbols, so brevity was key. But, I got used to using the shorter names. Consider which is clearer/better: for (fooidx = 0; fooidx <= 10; ++fooidx) Or: for (indexForFooArray = 0; indexForFooArray <= 10; ++indexForFooArray) I use the do { ... } while (0) to avoid if/else ladders a lot. This is called a "once through" loop. This is considered "controversial", but, in my experience it keeps the code cleaner. Personally, I've never found a [more standard] use of a do/while loop that can't be done more easily/better with a while or for loop--YMMV. In fact, a number of languages don't even have do/while at all. Also, I use lower case unless it's a #define [or enum] which is always upper. That is, I use "snake case" (e.g. fooidx) and not "camel hump case" (e.g. indexForFooArray). The .proto file containing function prototypes is auto-generated. This is a huge time saver. Side note: Be sure you have at least v2 from the external link as there was a bug in the Makefile. A make clean would erase the .proto. v2 won't do that I've developed my own style over the years. Turns out that the linux kernel style was "borrowed from mine". Not actually :-) Mine came first. But ... They, in parallel, came up with something that is a 99% match to mine: /usr/src/kernels/whatever_version/Documentation/CodingStyle. Consistency to a given style [one's own] is key. For a given function, you don't have to worry about what you'll name the variables, what indent or blank lines you'll use. This helps a reader/new developer. They can read a few functions, see the style in play, and then go faster because all functions have similar style. All this allows you to "go faster" and still get high quality code on the first try. I'm also quite experienced. Also, my code comments focus on "intent". That is, what do you want the code to do in real world terms. They should answer the "what/why" and the code is the "how".
Change inode ctime in Linux
utimensat() updates ctime when restoring atime and/or mtime, but I need to restore all 3 timestamps. I solved this in a tricky way, by saving the current clock, moving system clock to the needed ctime, calling utimensat(), restoring current clock. It does the job, but I understand this is a bad solution, so I'm looking for a better one, all in (root) userspace without kernel mods. Here is the code I wrote to verify utimensat() behaviour and test the solution. /* Test utimensat() behaviour, restore also Change timestamp */ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <time.h> #define MYFILE "/home/user1/Work/myfile.txt" static int get_timestamps(struct stat *sb, const char *descr) { sleep(1); if (stat(MYFILE, sb) == -1) { perror("stat(" MYFILE ") ERROR"); return(errno); } char *a = ctime(&sb->st_atime); char *m = ctime(&sb->st_mtime); char *c = ctime(&sb->st_ctime); printf("%s\n", descr); printf("Last file access: %.*s.%lu\n", (int)strlen(a)-1, a, sb->st_atim.tv_nsec); printf("Last file modification: %.*s.%lu\n", (int)strlen(m)-1, m, sb->st_mtim.tv_nsec); printf("Last status change: %.*s.%lu\n\n", (int)strlen(c)-1, c, sb->st_ctim.tv_nsec); return(0); } int main(int argc, char **argv) { int rc = 0; struct stat sb_original; struct stat sb; // Get file stats rc = get_timestamps(&sb_original, "Initial"); if (rc) return(rc); // Read file FILE *myfile = fopen(MYFILE, "r"); if (myfile == NULL) { perror("fopen(" MYFILE ") ERROR"); return(errno); } char buffer[10]; (void)fread(buffer, 1, 1, myfile); (void)fclose(myfile); rc = get_timestamps(&sb, "After fopen(); fread(); fclose()"); if (rc) return(rc); system("touch " MYFILE); rc = get_timestamps(&sb, "After touch"); if (rc) return(rc); // Restore original Last Access timestamp updated after fopen(); fread(); fclose() if (sb.st_ino) { int have_sudo = 0; struct timespec ts[2], tsctime, tsnow; ts[0].tv_sec = sb_original.st_atim.tv_sec; ts[0].tv_nsec = sb_original.st_atim.tv_nsec; ts[1].tv_sec = sb_original.st_mtim.tv_sec; ts[1].tv_nsec = sb_original.st_mtim.tv_nsec; tsctime.tv_sec = sb_original.st_mtim.tv_sec; tsctime.tv_nsec = sb_original.st_mtim.tv_nsec; if (clock_gettime(CLOCK_REALTIME, &tsnow) < 0) { perror("clock_gettime(CLOCK_REALTIME) ERROR"); return(errno); } /* ##### Fixme: is there a better way to restore all 3 timestamps? */ // Bad solution but does the job ;) if (clock_settime(CLOCK_REALTIME, &tsctime) < 0) { perror("clock_settime(CLOCK_REALTIME,ctime) ERROR"); if (errno != EPERM) return(errno); have_sudo = 1; } if (utimensat(0, MYFILE, ts, 0) < 0) { // This updates Change timestamp! perror("utimensat(" MYFILE ") ERROR"); return(errno); } if (have_sudo && clock_settime(CLOCK_REALTIME, &tsnow) < 0) { perror("clock_settime(CLOCK_REALTIME,now) ERROR"); return(errno); } rc = get_timestamps(&sb, "After utimensat()"); if (rc) return(rc); } return(0); }
Trying to use CreateProcess to output a file, but hangs on output?
For part of a project, I have to be able to output a file through stdout using CreateProcess. However, it seems to be waiting for input after it outputs. It works when I just run source.exe by itself. Main Program: #include <winbase.h> #define DELAY_A_WHILE() {volatile int j; for (j=0; j < 10000000; ++j) ; } #define BUFSIZE 4096; int main(int argc, char *argv[]) { ///VARS/// char cmdline1[MAX_PATH]; //Stores the command line for source. char cmdline2[MAX_PATH]; //Stores the command line for sink. STARTUPINFO StartupInfo; //Stores the startup information. PROCESS_INFORMATION ProcInfoSource, ProcInfoFilter, ProcInfoSink; // Filled in by CreateProcess // New Process Creation Flags: DWORD CreateFlags = 0; BOOL createStatus; HANDLE sourceFilterRead, sourceFilterWrite, filterSinkRead, filterSinkWrite; //Handles for the two pipes. ////////// //SECURITY_ATTRIBUTES PipeAttributes; //Pipe security attributes. //PipeAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); //PipeAttributes.lpSecurityDescriptor = NULL; // ignore //PipeAttributes.bInheritHandle = TRUE; // child can inherit ////////// //////////SOURCE// if (argc == 3) { strcpy(cmdline1, "Source.exe"); strcat(cmdline1, " "); strcat(cmdline1, argv[1]); //Parse the first command line for source.exe. strcpy(cmdline2, "sink.exe"); strcat(cmdline2, " "); strcat(cmdline2, argv[2]); //Parse the second command line for sink.exe. //if (!CreatePipe(&sourceFilterRead, &sourceFilterWrite, &PipeAttributes, 0)) { //Source | Filter // printf("Error creating pipe: %d\n", GetLastError()); //} ///STARTUP/// GetStartupInfo(&StartupInfo); /* StartupInfo.dwFlags |= STARTF_USESTDHANDLES; StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); printf("%s\n", cmdline1); printf("%s\n", cmdline2); */ createStatus = CreateProcess( NULL, cmdline1, NULL, NULL, TRUE, 0, NULL, NULL, &StartupInfo, &ProcInfoSource); if(createStatus == 0) { printf("Source process failed.\n"); return(-1); } CloseHandle(ProcInfoSource.hProcess); CloseHandle(ProcInfoSource.hThread); printf("Source success.\n"); } else printf("Arg error\n"); return(0); } Source.exe: #include <stdio.h> #include <windows.h> #include <winbase.h> #define DELAY_A_WHILE() {volatile int j; for(j=0; j<10000000; j++);} int main(int argc, char *argv[]) { int ch; FILE *fin; if(argc != 2 || fopen_s(&fin, argv[1], "rb")) { fprintf(stderr, "Usage: source inputfilename\n"); return(1); } //Read chars and copy to stdout. while(1) { if ((ch = fgetc(fin)) == EOF) break; fputc(ch, stdout); DELAY_A_WHILE(); } fclose(fin); return(0); } EDIT: I found a solution. If I use WaitForSingleObject() right after I start the process, it works properly. Not sure if there's a way to do it without it. The goal of the assignment is to have a bunch of processes working simultaneously without waiting for one another, but I have not found another way to do it without waiting for source.exe to finish.
Using pipe system call in a mini shell
Although my program works correctly in all cases, it doesn't use a pipe to connect the output of the first of two commands to the second when they're separated by a pipe symbol. I wrote the output of the first command to a file, then redirected the standard input of the second command to the file when the process to run that command was run. I need to use a pipe system call to create the pipe and obtain the file descriptors for the pipe, and then run the two processes at the same time. It is a homework question and I have done 99% of the work but somehow am not able to get the pipe system call working... what I've been trying is that for an input like: Command 1 | Command 2 inside the child process for command 2 I close FD[0] then dup FD[1] and for command 1 close FD[1] then dup FD[1] and close FD[0]. I am hell confused with the file descriptors when using pipe.... I have to use a pipe Any sort of help is appreciated. Execute function is where I am forking the processes. Here's my code... #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> #include <regex.h> /* Global Variables */ extern char **environ; /* Environmental Variables */ char *pathList[10]; /* List of paths from the $PATH */ int pathCount; /* Count of the # of paths in $PATH */ char *pathSet; /* Variable through which $PATH is retrieved */ int hasPipe = 0; int cmdNo = 0; /* This function takes the 'finalPath', the full path to executable,argList[],the full command-line input arguments and argCount, the number of arguments from command-line as input. It the creates a child process, in turn invokes the execve() that finally executes the executable in 'finalPath' with the arguments in 'argText' all stored into the args[] appropriately. Child process also handles input and output file re-direction. */ void execute(char *finalPath, char *argList[], int argCount) { int k,fd,ofound,pos,i; /* flags and temporary variables */ pid_t pid; /* process ID */ int status, which; char msg[100]; char *args[4]; /* argument list for execve() */ int spCase = 0; ofound = 0; pos=0; pid = fork(); /* Creating a new process using fork() */ if (pid == -1) /* Checking for errors in process creation */ { write(1,"Fork failed.\n",12); exit(1); } /************************** Checking for parent process ***************************/ if (pid != 0) { which = wait(&status); if (which == -1) { write(1,"Wait failed.\n",12); exit(1); } if (status & 0xff) { /* Case of abnormal termination */ sprintf(msg,"ERROR: <dShell> # process %d terminated abnormally for reason %d\n",which, status & 0xff); write(1,msg,strlen(msg)); } else { /* Case of normal termination */ sprintf(msg,"process %d terminated normally with status %d\n",which, (status >> 8) & 0xff); write(1,msg,strlen(msg)); } } /************************* Checking for child process **************************/ if (pid == 0) { char argText[50]; argText[0] = '\0'; int std_fd; if(cmdNo==0 && hasPipe) { close(1); std_fd = open("temp.out", O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU); dup(std_fd); } else if(cmdNo==1 && hasPipe) { close(0); std_fd = open("temp.out", O_RDONLY); dup(std_fd); } /* Finding the first re-direction operator */ for( i = 0; i < argCount ; ++i) { if( ofound != 1 && ofound != 2) { if( strcmp(argList[i],"<") == 0 ) { fd = open(argList[i+1],O_RDONLY); if (fd < 0) { sprintf(msg,"ERROR: %s could not be opened\n", argList[i+1]); write(1, msg, strlen(msg)); exit(5); } ofound = 1; strcpy(argText,"\0"); close(0); dup(fd); close(fd); } else if(strcmp(argList[i],">") == 0) { fd = open(argList[i+1],O_CREAT | O_WRONLY, 0777); pos = i; ofound = 2; strcpy(argText,"\0"); if (fd < 0) { sprintf(msg,"ERROR: %s could not be opened\n", argList[i+1]); write(1, msg, strlen(msg)); exit(5); } close(1); dup(fd); close(fd); } } } /* If input re-direction operator is found check for an output re-direction along with it */ if(ofound == 1) { for( k = 0; k < argCount && ofound != 2; ++k) { if( strcmp(argList[k],">") == 0 ) { fd = open(argList[k+1],O_CREAT | O_WRONLY , 0777); spCase = 1; ofound = 2; strcpy(argText,"\0"); if (fd < 0) { sprintf(msg,"ERROR: %s could not be opened\n", argList[k+1]); write(1, msg, strlen(msg)); exit(5); } close(1); dup(fd); close(fd); } } } /* If the re-direction operators are not found */ if( ofound == 0 ) { for(i = 1; i < argCount; ++i) { strcat(argText, argList[i]); strcat(argText, " "); } spCase = 2; } /* Case when both arguments and output re-direction operators are found */ if (spCase == 0) { if(pos == 0) { for( i = 3; i<argCount; ++i) { strcat(argText, argList[i]); strcat(argText," "); } } if(pos == argCount - 2) { for( i = 1; i<argCount - 2; ++i) { strcat(argText, argList[i]); strcat(argText," "); } } } argText[strlen(argText)-1] = '\0'; /*because I added an extra space so trimming that*/ /* Running the execve */ args[0] = finalPath; if(strlen(argText) == 0) /* checking if argText is populated */ { args[1] = NULL; } else { args[1] = argText; args[2] = NULL; } /* Execute command,if it returns that means it failed and need to display error and exit */ execve(args[0], args, environ); sprintf(msg, "ERROR! execve() failed"); write(1, msg, strlen(msg)); } } /******************************************************************************* This function checks if the path is accessible and continues to execute the command. If the path does not exist of is not accessible, variable 'retFlag' is used to return 0 to the calling function. ********************************************************************************/ int checkPath(char *exepath, char *argList[], int argCount, int flag) { char *finalPath; int retFlag = flag; if(access(exepath,X_OK) == 0) { finalPath = exepath; retFlag = 1; execute(finalPath,argList,argCount); return retFlag; } else return retFlag; } /********************************************************************************** This function checks if the first argument is a path and if so calls checkPath(). Else it gets the paths set to the $PATH variable, tokenizes it, pads it with the first token of input command and calls checkPath(). If the correct path is established, the variable 'found' is used to kick out of the for loop. ************************************************************************************/ void setPath(char *argList[], int argCount) { char *exepath; char com[50]; char emsg[80]; char *command; int i,found = 0; /* Seperating the command if redirection is used */ if( strcmp(argList[0],"<") == 0 || strcmp(argList[0],">") == 0 ) { command = argList[2]; } else command = argList[0]; /* In case of no redirection, storing the commands and arguments into a array */ if(strcmp(command,"#") == 0) /* Checking for comment statements */ { write(1,"ERROR: No command(s) found. Only comment present/n",48); } else { if(strstr(command,"/")) /* Checking if the entire path is given as a part of the command */ { exepath = command; found = checkPath(exepath,argList,argCount,0); } else /* building the path and storing it in 'com' */ { for(i = 0; i< pathCount && found != 1; i++) { sprintf(com,"%s%s%s",pathList[i],"/",command); exepath = com; found = checkPath(exepath,argList,argCount,0); } } if(found == 0) { sprintf(emsg,"%s%s",command,":COMMAND DOES NOT EXIST"); write(1,emsg,sizeof(emsg)); write(1,"\n",1); } } } /* Tokenizes commands into words */ void tokens(char *cmdStr) { char cmd[100]; strcpy(cmd,cmdStr); char *result; char delims[] = " , "; char *argList[20]; int argCount = 0; /*Tokenize the individual command into strings */ result = strtok(cmd,delims); while( result != NULL ) { argList[argCount] = result; result = strtok( NULL, delims ); ++argCount; } setPath(argList,argCount); } /* Tokenizes multiple commands into single commands */ void tokenize(char *inputStr) { int i,cmdCount = 0; char *cmdResult; char *cmdStr[100]; char delimiters[] = "|"; cmdResult = strtok(inputStr, delimiters); while(cmdResult != NULL) { cmdStr[cmdCount]=cmdResult; cmdResult = strtok(NULL, delimiters); cmdCount++; } if( cmdCount > 1 ) hasPipe = 1; else hasPipe = 0; for( i=0; i<cmdCount ; i++) { cmdNo = i%cmdCount; tokens(cmdStr[i]); } } int main(int argc, char *argv[]) { char prompt[8]; /* String that stores the personalized prompt */ char *path; /* Temporary variable used for tokenization*/ char ch; /* Temporary variable used in read() */ int chCount; /* # of characters read from the prompt */ int entry; /* return variable of read() */ int flag; /* Flag to go read the next command when newline is found */ regex_t reIgnore; char pattern[20]="^\\s*$|^#.*"; /* Tokenizing the paths asociated with the $PATH and storing them in a array declared globally */ pathCount = 0; pathSet = getenv("PATH"); if ( !pathSet) { write(1, "ERROR: PATH environment does not exist.\n", 40); exit(1); } path = strtok(pathSet,":"); while(path != NULL) { pathList[pathCount] = path; path = strtok(NULL,":"); ++pathCount; } /* Checks for blanks and tabs in Step 2 */ if ( regcomp(&reIgnore, pattern, REG_EXTENDED) ) { write(1, "Error. \n",9); exit(2); } sprintf(prompt,"<dShell> # "); /* Storing the personalized shell prompt into 'prompt' */ /* Reading the input from command line and passing it to tokenize() */ while(1) { char inputStr[100]; /* String into which inputs are read into */ chCount = 0; flag = 0; hasPipe = 1; write(1,prompt,strlen(prompt)); /* Printing out the personalized shell prompt */ /* This will read a character 1 by 1 until it reaches the end of file */ entry = read(0,&ch,1); if(!entry) exit(0); /* Reading the input and storing it in inputStr as long as newline is not encountered */ while( entry != 0 && flag == 0 ) { /* A newline has been found so a new command will need to be executed */ /* The inputStr till this point is sent to tokenize() */ if( ch == '\n' ) { inputStr[chCount] = '\0'; flag = 1; if(chCount > 0) { if(strcmp(inputStr,"exit") == 0) exit(3); else tokenize(inputStr); } } inputStr[chCount] = ch; chCount++; if(flag == 0) entry = read( 0, &ch, 1 ); } } }
See the man page for pipe(2). It has this example: #include <sys/wait.h> #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main(int argc, char *argv[]) { int pipefd[2]; pid_t cpid; char buf; assert(argc == 2); if (pipe(pipefd) == -1) { perror("pipe"); exit(EXIT_FAILURE); } cpid = fork(); if (cpid == -1) { perror("fork"); exit(EXIT_FAILURE); } if (cpid == 0) { /* Child reads from pipe */ close(pipefd[1]); /* Close unused write end */ while (read(pipefd[0], &buf, 1) > 0) write(STDOUT_FILENO, &buf, 1); write(STDOUT_FILENO, "\n", 1); close(pipefd[0]); _exit(EXIT_SUCCESS); } else { /* Parent writes argv[1] to pipe */ close(pipefd[0]); /* Close unused read end */ write(pipefd[1], argv[1], strlen(argv[1])); close(pipefd[1]); /* Reader will see EOF */ wait(NULL); /* Wait for child */ exit(EXIT_SUCCESS); } }