This is likely to be in several parts. I hope that's ok.
Each part has some interesting features.
Features: Running bash commands from C, write and run a bash shell script, a script that 'launches' an app (our own app in this case), a couple of kdialog shell commands.
Before I go back and re-create my kdevelop3 deb installation a bit
more correctly, let's see what we can do with a little C programming.
It uses shell commands, so that part should be interesting to script
writers too.
Let's make a daemon to launch, send data to, and watch a ProgressBar
that we will talk to (for now) through a regular file in the /tmp
directory.
Our connection to the daemon will be (essentially) its pid. It's
connection to the ProgressBar will be the dbus connection returned
by kdialog --progressbar.
Overview:
* bash
addr=$(ProgressBar title total)
...
to increment:
let serial=serial+1
echo "$serial "add" $chunksize" > $addr
to stop:
let serial=serial+1
echo $serial "stop" 0 > $addr
to end:
if ! -x $addr # file has been deleted/connection closed
to pause:
todo
* ProgressBar program
init
send file address = /tmp/ProgressBar-$pid
create file
set up variables
read pipe "kdialog --title \"%s\" --progressbar %s"
save dbus address
run
begin daemon loop
check file serial new?
update widget
check widget
closed?
end loop
check file
removed?
end loop
cleanup
remove file
Part 1 testing the self-launcher.
First let's create an app that can 'launch' itself independent
of a terminal. In this test, notice that you can execute
terminal commands after the app has launched itself and before
it quits (after 10 seconds).
THIS TEST TAKES 10 SECONDS TO FINISH
You might be confused by the launched app poping a notice up
after you thought the test was done if I hadn't warned you.
To compile type:
[The gnu99 switch allows index vars to be defined locally inside for(...) loops.]
To run type:
[add the old './' prefix if you don't have '.' (dot) in your path.]
_________________________________________________
Note: This is REALLY causing the app to re-launch itself with 'init' as its parent. You can verify this in ksysguard. You have 10 seconds to look for it. Currently it isn't doing anything for that ten seconds except waiting. It could just as well be polling the "connection" from the terminal to update variables for the ProgressBar.
Each part has some interesting features.
Features: Running bash commands from C, write and run a bash shell script, a script that 'launches' an app (our own app in this case), a couple of kdialog shell commands.
Before I go back and re-create my kdevelop3 deb installation a bit
more correctly, let's see what we can do with a little C programming.
It uses shell commands, so that part should be interesting to script
writers too.
Let's make a daemon to launch, send data to, and watch a ProgressBar
that we will talk to (for now) through a regular file in the /tmp
directory.
Our connection to the daemon will be (essentially) its pid. It's
connection to the ProgressBar will be the dbus connection returned
by kdialog --progressbar.
Overview:
* bash
addr=$(ProgressBar title total)
...
to increment:
let serial=serial+1
echo "$serial "add" $chunksize" > $addr
to stop:
let serial=serial+1
echo $serial "stop" 0 > $addr
to end:
if ! -x $addr # file has been deleted/connection closed
to pause:
todo
* ProgressBar program
init
send file address = /tmp/ProgressBar-$pid
create file
set up variables
read pipe "kdialog --title \"%s\" --progressbar %s"
save dbus address
run
begin daemon loop
check file serial new?
update widget
check widget
closed?
end loop
check file
removed?
end loop
cleanup
remove file
Part 1 testing the self-launcher.
First let's create an app that can 'launch' itself independent
of a terminal. In this test, notice that you can execute
terminal commands after the app has launched itself and before
it quits (after 10 seconds).
THIS TEST TAKES 10 SECONDS TO FINISH
You might be confused by the launched app poping a notice up
after you thought the test was done if I hadn't warned you.
Code:
// file: main.c // some commonly used c headers #include <stdio.h> // printf, sprintf, etc. #include <stdlib.h> // exit() #include <unistd.h> // getpid() getppid()..tons of stuff #include <malloc.h> // memory allocation #include <string.h> // strings and block comparisons and moves #include <errno.h> // error numbers #include <stddef.h> // pid/ppid #include <stdlib.h> // canonicalize_file_name() void dbg(){} // a breakpoint for kdbg.exec char scratch[4096]; // recreate commandline from parsed args list int unparse_args(char* cmdline, int limit, int argc, char** argv); // launch (init) self using restored commandline int launch_self(char* cmdline); // write a file, possibly executable, from a string/buffer. int write_file(const char* path, const char* fname, char* data, int len, int exec); int main(int argc, char** argv) { dbg(); if(getppid() != 1) { // if not parent = init char *ptmp, *pstr = scratch; int len, err; // get full path to app, even if ./app used and create // shell script text to 'launch' this app again. ptmp = canonicalize_file_name(argv[0]); pstr += sprintf(pstr, "#/!bin/sh\n" "nohup %s ", ptmp); free(ptmp); len = pstr - scratch; err = unparse_args(pstr, 4096-len, argc, argv); if(err) { perror("Can't unparse args"); return err; } // update string printer var position and // suppress error and warning messages and // return control to terminal immediately with // the shell '&' operator. pstr += strlen(pstr); pstr += sprintf(pstr, " >/dev/null 2>&1 &"); // use kdialog functions to report current status.. system("kdialog --msgbox 'ready to launch self'"); return launch_self(scratch); } // use kdialog functions to report current status in launched app system("kdialog --passivepopup 'self is now running\nPlease wait... 10 seconds' '5'"); sleep(10); system("kdialog --msgbox 'self is now stopping after running 10 seconds'"); return 0; } // recreates a commandline from an args list int unparse_args(char* cmdline, int limit, int argc, char** argv) { cmdline[0] = 0; if(argc < 2) return 0; char* buf = (char*) malloc(limit+256); if(!buf) return errno = ENOMEM; char* p = buf; char* stopat = buf+limit -1; int len; for(int i = 1; i < argc; i++) { p += sprintf(p, "%s ", argv[i]); if(p >= stopat) { free(buf); return errno = E2BIG; } } p[-1] = 0; // terminate memcpy(cmdline, buf, p-buf); free(buf); return 0; } // print an error message and return error code int errmsg(int errcode, const char* msg) { fprintf(stderr, "%s\n", msg); return errcode; } // write a shell script that will execute a commandline, returning // control to the terminal or calling app immediately. Parent of // the newly launched app will be 'init'. int launch_self(char* cmdline) { char cmd[256]; // if(write_file(getenv("HOME"), "tmp.sh", scratch, strlen(scratch), true)) if(write_file(getenv("HOME"), "tmp.sh", scratch, strlen(scratch), 1)) return errmsg(1, "Can't write launcher script"); // ok? now let's execute the script sprintf(cmd, "%s/tmp.sh", getenv("HOME")); system(cmd); return 0; } // write a file from data, optionally executable int write_file(const char* path, const char* fname, char* data, int len, int exec) { char fullname[256]; char cmd[256]; sprintf(fullname, "%s/%s", path, fname); FILE* fp = fopen(fullname, "w"); if(!fp) return 1; fwrite(data, 1, len, fp); fclose(fp); if(exec) { sprintf(cmd, "chmod +x %s", fullname); return system(cmd); } else return 0; }
gcc -std=gnu99 main.c -o main
To run type:
main
_________________________________________________
Note: This is REALLY causing the app to re-launch itself with 'init' as its parent. You can verify this in ksysguard. You have 10 seconds to look for it. Currently it isn't doing anything for that ten seconds except waiting. It could just as well be polling the "connection" from the terminal to update variables for the ProgressBar.
Comment