I wrote a linux program as below:
int g_para1 = 10;
int g_para2 = 11;
void SetPara(int para1, int para2)
{
g_para1 = para1;
g_para2 = para2;
}
void DumpPara()
{
printf("g_para1 = %d, g_para2 = %d\n", g_para1,g_para2);
}
void init()
{
int pid;
if(pid = fork())
exit(0);
else if(pid < 0)
exit(1);
setsid();
if(pid = fork())
exit(0);
else if(pid < 0)
exit(1);
return;
}
int main()
{
signal(SIGCHLD, SIG_IGN);
init();
while(1)
{
DumpPara();
sleep(10);
}
return 0;
}
And then compile and run it in the shell
gcc -o test test.c
./test
it will show the print "g_para1 = 10, g_para2 = 11" every 10 seconds.
My question is:
If I execute "SetPara 20, 30" in the shell, i want the print shows "g_para1 = 20, g_para2 = 30".
What should i do to make it work?
If I do nothing, it will show
SetPara 20,30
SetPara: command not found
First, an application (in C, compiled with debug info, e.g. with gcc -Wall -g) can be remotely debugged. If you have ssh available, you simply debug using gdb thru a terminal connection via ssh. Otherwise, read the documentation of GDB, it has a chapter on remote debugging.
Then, it looks like you wish to embed your application into a scripting language interpreter. Then try lua, or GNU guile, or perhaps even python or ocaml. Notice that embedding your application inside an interpreter (or symetrically, putting an interpreter inside your application) is a heavy architectural decision and has major impacts on its design.
BTW, some Unix shells can be extended thru plugins, e.g. zsh (with modules...). Plugins use dynamic loading facilities like dlopen(3) (you might consider using them in your application).
But an ordinary shell can only start external programs using fork(2) & execve(2)
BTW, you might consider making your application behave as a Web server by using some HTTP server library inside it, e.g. libonion. You could also consider remote procedure call techniques, e.g. JSON RPC
Read also Advanced Linux Programming
As for your improved question, just define a convention in main arguments: you could want that if you invoke your program with arguments --setpara 23 45 it will call SetPara(23,45) e.g. with code like:
int main (int argc, char**argv) {
if (argc>3 && !strcmp[argv[1], "--setpara")) {
int x = atoi(argv[2]);
int y = atoi(argv[3]);
SetPara (x,y);
}
but you should do serious arguments passing e.g. with getopt_long (and accept --help & --version). See this answer and that one.
If you want SetPara to be called elsewhere in your code, the arguments to main should trigger other behavior (e.g. initializing global data, initializing an embedded interpreter, etc.).
Notice that each process has its own address space in virtual memory. See also this answer.
PS: your question is very unclear; I tried to give various ways of approaching your issues, which I am mostly guessing. You should study -for inspiration at least- the source code of some existing free software related to your goals.
Related
I have a C program, that I would like to print its output from swift, and when it scans I can give it input through Swift. Is such thing possible? I tried this with a simple function, and it worked, but how can someone do so with many different functions that call other functions?
I know the question is a bit vague, but can someone point me into the right direction?
Example of code:
int main(int argc, char **argv) {
int i;
int hitme;
char ch;
prelim();
if (argc > 1) { // look for -f option
if (strcmp(argv[1], "-f")== 0) {
coordfixed = 1;
argc--;
argv++;
}
}
if (argc > 1) {
fromcommandline = 1;
line[0] = '\0';
while (--argc > 0) {
strcat(line, *(++argv));
strcat(line, " ");
}
}
else fromcommandline = 0;
while (TRUE) { /* Play a game */
setup();
if (alldone) {
score(0);
alldone = 0;
}
else makemoves();
skip(2);
stars();
skip(1);
if (tourn && alldone) {
printf("Do you want your score recorded?");
if (ja()) {
chew2();
freeze(FALSE);
}
}
printf("Do you want to play again?");
if (!ja()) break;
}
skip(1);
prout("May the Great Bird of the Galaxy roost upon your home planet.");
return 0;
}
Yes.
This is extensively covered in Using Swift with Cocoa and Objective-C. Objective-C is a superset of C, so all the instructions for Objective-C work equally well for C.
The short version is that you just add the C code to your project, import its header in your Objective-C Bridging Header, and then the C functions will be available in Swift (using various automatic translations).
That said, if you really want to read the output (i.e. the results of these printf) calls, that's a bit different problem. I'd avoid it if you can. Otherwise you'd need to do something like build the C program as its own executable and use NSTask within Swift to call it and capture the output, or you'd have to hijack stdout with something like fdopen. It's a pain to do that completely correctly.
I will focus on the second part of your question, how to interact with C code that uses the standard IO facilities:
The obvious choice as Rob Napier pointed out is just compiling the C code into an executable and using something akin to popen(3) to read and write to its standard IO facilities, the same way you would read/write any other FILE*.
Another way would be to seek out places where stdio is used and change these functions. For example you could use
#ifdef STANDALONE
#define print printf
#else
#define print passToSwift
#endif
Then you can change all the printfs to prints and just #define which mode you want your C code to operate in. In case STANDALONE is left undefined, you will have to provide a passToSwift function that will connect your C and Swift functionality.
One more way without having to change all printfs is using funopen(3) or friends, particularly fwopen(3). With fwopen(3) (man fwopen) you can provide a passToSwift function to be called whenever something is written to stdout.
#include <stdio.h>
int passToSwift(void * cookie, const char * buffer, int len)
{
(void)cookie;
// do stuff with the buffer you recieved
return len;
}
int main(void)
{
fflush(stdout);
stdout = fwopen(NULL, passToSwift);
printf("Hey\n");
}
The assignment to stdout is not portable, but works for me on OS X. I am not aware of any other way to achieve it. (dup2 gives EBADF for funopend streams, freopen expects an entry in the filesystem).
I am adressing a quite similar problem.
I have a solution open to discussion on codereview: C hack: replace printf to collect output and return complete string by using a line buffer
Maybe you could use that (or a part of it) for your text game as well ...
The improved version of C hack: replace printf to collect output and return complete string by using a line buffer is now availabe on github as Xcode 7 project swift-C-string-passing (and standalone gcc version).
Especially look at the #define preprocessor statements to make use of the bridge to swift (similar to a3f's answer).
My solution is able to pass strings in and out to the C code. But how are the answers retrieved from the user? I.e. what does the ja() function do?
I'd like to write a C program on FreeBSD 10.1 that implements a DTrace consumer using libdtrace.
I know that I need to start with a call to dtrace_open() - e.g. I have found this old presentation, but I can't get even started, since there is no dtrace.h installed (only in the system source tree).
The shared library is installed, e.g. the /usr/sbin/dtrace tool included with FreeBSD can act as a DTrace consumer and this tool links to /lib/libdtrace.so.2 (which is also pointed to via a symbolic link from /usr/lib/libdtrace.so).
Any basic example, including build instructions (FreeBSD 10.1 / clang) will help me a lot.
The actual goal of writing a custom consumer is creating a CFFI based wrapper usable in Python and PyPy. Means: above C program is just to get started, learn and then proceed.
CFFI is the recommended, modern, high-performance way of interfacing PyPy with shared libraries.
CFFI can be used at the ABI and API level. The latter requires a header file to include, the former requires to declare the stuff used from a lib.
Adapted from Adam's answer, here is a complete example that works on FreeBSD 10.1.
Makefile:
all:
cc \
-I /usr/src/cddl/compat/opensolaris/include \
-I /usr/src/cddl/contrib/opensolaris/lib/libdtrace/common/ \
-I /usr/src/sys/cddl/compat/opensolaris \
-I /usr/src/sys/cddl/contrib/opensolaris/uts/common/ \
hello_dtrace.c \
-l dtrace -l proc -l ctf -l elf -l z -l rtld_db -l pthread -l util \
-o hello_dtrace
hello_dtrace.c:
#include <dtrace.h>
#include <signal.h>
#include <stdio.h>
static dtrace_hdl_t* g_dtp;
static int chewrec (const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg) {
printf("chewing dtrace record ..\n");
// A NULL rec indicates that we've processed the last record.
if (rec == NULL) {
return (DTRACE_CONSUME_NEXT);
}
return (DTRACE_CONSUME_THIS);
}
static const char* g_prog = "BEGIN { printf(\"hello from dtrace\\n\"); }";
//static const char* g_prog = "syscall::open*:entry { printf(\"%s %s\\n\", execname, copyinstr(arg0)); }";
static int g_intr;
static int g_exited;
static void intr (int signo) {
g_intr = 1;
}
int main (int argc, char** argv) {
int err;
if ((g_dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) {
fprintf(stderr, "failed to initialize dtrace: %s\n", dtrace_errmsg(NULL, err));
return -1;
}
printf("Dtrace initialized\n");
(void) dtrace_setopt(g_dtp, "bufsize", "4m");
(void) dtrace_setopt(g_dtp, "aggsize", "4m");
printf("dtrace options set\n");
dtrace_prog_t* prog;
if ((prog = dtrace_program_strcompile(g_dtp, g_prog, DTRACE_PROBESPEC_NAME, 0, 0, NULL)) == NULL) {
fprintf(stderr, "failed to compile dtrace program\n");
return -1;
} else {
printf("dtrace program compiled\n");
}
dtrace_proginfo_t info;
if (dtrace_program_exec(g_dtp, prog, &info) == -1) {
fprintf(stderr, "failed to enable dtrace probes\n");
return -1;
} else {
printf("dtrace probes enabled\n");
}
struct sigaction act;
(void) sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = intr;
(void) sigaction(SIGINT, &act, NULL);
(void) sigaction(SIGTERM, &act, NULL);
if (dtrace_go(g_dtp) != 0) {
fprintf(stderr, "could not start instrumentation\n");
return -1;
} else {
printf("instrumentation started ..\n");
}
int done = 0;
do {
if (!g_intr && !done) {
dtrace_sleep(g_dtp);
}
if (done || g_intr || g_exited) {
done = 1;
if (dtrace_stop(g_dtp) == -1) {
fprintf(stderr, "could not stop tracing\n");
return -1;
}
}
switch (dtrace_work(g_dtp, stdout, NULL, chewrec, NULL)) {
case DTRACE_WORKSTATUS_DONE:
done = 1;
break;
case DTRACE_WORKSTATUS_OKAY:
break;
default:
fprintf(stderr, "processing aborted");
return -1;
}
} while (!done);
printf("closing dtrace\n");
dtrace_close(g_dtp);
return 0;
}
To run:
[oberstet#brummer2 ~/hello_dtrace]$ make; sudo ./hello_dtrace
cc -I /usr/src/cddl/contrib/opensolaris/lib/libdtrace/common/ -I /usr/src/sys/cddl/compat/opensolaris -I /usr/src/sys/cddl/contrib/opensolaris/uts/common/ hello_dtrace.c -l dtrace -l proc -l ctf -l elf -l rtld_db -l z -l pthread -l util -o hello_dtrace
Dtrace initialized
dtrace options set
dtrace program compiled
dtrace probes enabled
instrumentation started ..
chewing dtrace record ..
hello from dtrace
chewing dtrace record ..
^Cclosing dtrace
The libdtrace API isn't necessarily intended for stable consumers, but it's pretty easy to learn from some existing consumers to get started. The simplest and most modern is plockstat, the illumos utility for user-land locking statistics.
Here's the basic flow of a simple DTrace consumer program:
dtrace_open() to get a dtrace_hdl_t, a handle for other libdtrace interactions
dtrace_setopt() to configure options (-x flag to dtrace(1M))
dtrace_strcompile() to compile a string of your D program
dtrace_program_exec() to send that program to the kernel
dtrace_go() to instrument the system and start recording data
dtrace_close() to clean up at the end
For the main data collection loop (between dtrace_go() and dtrace_close()) do the following:
dtrace_sleep() to pause according to the DTrace options (switchrate and aggrate)
dtrace_work() to process traced data
dtrace_stop() to abort
See the main loop in plockstat.c for more.
For other simple DTrace consumers, check out intrstat and lockstat. For the kitchen sink, check out the code the the dtrace(1M) command-line utility.
dtrace is considered a system internal interface, and writing a custom consumer is not something that is supported.
The best you can do is check out a copy of the FreeBSD source tree and build your code from there. This actually isn't terribly difficult. Obviously, dtrace(1) is the canonical dtrace consumer, so you can look at its implementation for examples of how to use libdtrace. Additionally dtrace.h has a huge amount of comments explaining data structures and internals.
Building the files in the context of the source tree is pretty easy; the FreeBSD source tree layout is simple and writing Makefiles to build binaries in a self-contained manner is basically given to you "for free".
Pertinent points:
Clone the dtrace(1) Makefile into a new directory in your source tree checkout. Modify the Makefile such that .PATH is correct, and set PROG and SRCS to include the set of sources comprising your custom consumer.
Clone dtrace.c to your source directory (whereever you pointed .PATH to) and make changes as needed. Although the source is nearly 2,000 lines, much of it is support code. If you're just looking to clone a subset of the functionality, you'll find that most of the options to the binary are implemented in self-contained functions, and it should therefore be fairly easy to trim dtrace.c down to a bare-minimum form.
Without knowing what specifically you're needing to do with your custom consumer, it's difficult to tell you what else you'll need to call into. I'm assuming you'll probably want compile_file in there as well as exec_prog. Of course, your needs may differ.
The dtrace.h you will be using has a number of comments about the various interfaces provided.
Hopefully this is enough to get you started; unfortunately, there's not a way to do what you want from a vanilla install. You could, of course, hack up the relevant Makefiles to install the necessary header files and create your own internal distribution. But that seems like more pain than it's worth.
I have program that needs to run other programs. It works fine when ran from normal terminal session. When ran with initscript, it doesnt get normal shell environment and most programs fail. So how can i get it to work with initscript?
vixie-cron seems to use execle() and pass envp as argument. But im having hard time to figure out how does it get the shell env settings.
Here is the current code that doesnt work properly with initscript:
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
} else if (pid == 0) {
execl("/bin/sh", "/bin/sh", "-c", cmd, (char *) NULL);
exit(EXIT_FAILURE);
}
EDIT: Something strange happened. Now the same program runs fine even when started by init script. Im sorry, this was kind of useless question. Anyways i got good answers. Thank you for help.
Environment variables are passed to and accessed by your program with the optional third main() argument. Simply prototype your main function like this :
int main(int argc, char *argv[], char *envp[])
... to gain access to these variables.
You can then pass it directly to the exec*e() family functions.
You can see this documented in the execve(2) man page.
It looks like this just sends a ping, but whats the point of that when you can just use ping?
/* WARNING: this is someone's attempt at writing a malware trojan. Do not
compile and *definitely* don't install. I added an exit as the
first line to avoid mishaps - msw */
int main (int argc, char *argv[])
{
exit(1);
unsigned int pid = 0;
char buffer[2];
char *args[] = {
"/bin/ping",
"-c",
"5",
NULL,
NULL
};
if (argc != 2)
return 0;
args[3] = strdup(argv[1]);
for (;;)
{
gets(buffer); /* FTW */
if (buffer[0] == 0x6e)
break;
switch (pid = fork())
{
case -1:
printf("Error Forking\n");
exit(255);
case 0:
execvp(args[0], args);
exit(1);
default:
break;
}
}
return 255;
}
It's a hack - or an attempt at a hack - to get arbitrary code run in a privileged mode. Ping needs to run SUID root to get a raw socket for an ICMP_ECHO_REQUEST and the intentional buffer overrun in gets(buffer) is intended to pass junk to ping.
I don't see how this could work in practice, but you shouldn't compile and run it.
It makes sure that ping is called with the arguments -c 5. Which is stupid, because a shell script or alias would be easier to read and faster to write.
This program basically emulates a simple shell program. A shell program is going to take the arguments of another program as input and launch that specified program in a new process. The program you have above is just hard coded for one specific program (ping in this case) and is very simple.
A shell program makes working with the operating system more user friendly by providing an interface to boot up programs.
Can you explain to me how I can do a mini program that does a system call in C in order to format the disk and create a new partition?
O/S is LynxOS.
Which commands would you execute at the shell?
Superficially, you could use some variant on this:
#include <stdlib.h>
static const char *cmds[] =
{
"command 1 with options",
"command 2 with different options",
0,
};
int main(void)
{
int i;
for (i = 0; cmds[i] != 0; i++)
if (system(cmds[i]) != 0)
exit(EXIT_FAILURE);
return(EXIT_SUCCESS);
}
I assume that the commands will provide appropriate diagnosis of any problems.
If you need to control the arguments, then you have more work to do.
The main caveat is "is this the disk that the o/s is running on", because if so, the chances are that the formatting of that disk will stop the programs from running successfully.