Realize a console in a GTK3 GUI programming in C - c

I realized a GUI with GTK3 that, essentially, generates an input text file for an exe program that with these inputs can do elaborations.
This exe is put in executions in the GUI by mean of a System call ( system("exe input.dat &") ).
This exe can print on screen message of information or error.
What I want to do is redirect these message on a GtkTextView.
The idea that I had is to redirect output and error on a file ( system("exe input.dat > output_file.txt 2>&1 &") ) and in the GUI read line by line this
file and send this strings in the textView.
I was not sure that 2 process can write and read the same file and to test this concept I used these 2 simple programs:
the writer (used like ./writer > out_file.txt):
#include <stdio.h>
#include <unistd.h>
main()
{
int a;
while(1)
{
fprintf(stdout,"a=%d\n",a);
fflush(stdout);
sleep(1);
a++;
}
}
and the reader:
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fp;
fp = fopen("out_file.txt","r");
char string_new[1024];
char string_old[1024];
strcpy(string_old," ");
while(1)
{
fgets(string_new,1024,fp);
if ( strlen(string_new) != 0 )
{
if ( strcmp(string_new, string_old) != 0 )
{
fprintf(stdout,"%s",string_new);
fflush(stdout);
strcpy(string_old,string_new);
}
}
}
}
This two programs run correctly and the second one print the output of the first one.
Putting in the GUI a similar code, the GUI read only the first line of the file.
How I can solve this issue?
Thank you

You should use popen instead of executing system("exe input.dat &"), then it's easy to read from the stdout output of the program.
Like this:
#include <stdio.h>
int main(void)
{
FILE *fp = popen("ls -lah /tmp", "r");
if(fp == NULL)
return 1;
char buffer[1024];
int linecnt = 0;
while(fgets(buffer, sizeof buffer, fp))
printf("Line: %d: %s", ++linecnt, buffer);
putchar('\n');
fclose(fp);
return 0;
}
which outputs:
$ ./b
Line: 1: total 108K
Line: 2: drwxrwxrwt 8 root root 12K Mar 10 02:30 .
Line: 3: drwxr-xr-x 26 root root 4.0K Feb 15 01:05 ..
Line: 4: -rwxr-xr-x 1 shaoran shaoran 16K Mar 9 22:29 a
Line: 5: -rw-r--r-- 1 shaoran shaoran 3.6K Mar 9 22:29 a.c
Line: 6: -rw------- 1 shaoran shaoran 16K Mar 9 22:29 .a.c.swp
Line: 7: -rwxr-xr-x 1 shaoran shaoran 11K Mar 10 02:30 b
Line: 8: -rw-r--r-- 1 shaoran shaoran 274 Mar 10 02:30 b.c
Line: 9: -rw------- 1 shaoran shaoran 12K Mar 10 02:30 .b.c.swp
Line: 10: drwx------ 2 shaoran shaoran 4.0K Mar 9 20:08 firefox_shaoran
Line: 11: drwxrwxrwt 2 root root 4.0K Mar 9 20:06 .ICE-unix
Line: 12: srwx------ 1 mongodb mongodb 0 Mar 9 20:07 mongodb-27017.sock
Line: 13: prwx------ 1 shaoran shaoran 0 Mar 9 20:08 oaucipc-c2s-1874
Line: 14: prwx------ 1 shaoran shaoran 0 Mar 9 20:08 oaucipc-s2c-1874
Line: 15: drwxrwxr-x 2 root utmp 4.0K Mar 9 20:06 screen
Line: 16: drwx------ 2 shaoran shaoran 4.0K Mar 9 20:07 ssh-XueH0w8zWCSE
Line: 17: drwx------ 2 shaoran shaoran 4.0K Mar 9 20:08 thunderbird_shaoran
Line: 18: -r--r--r-- 1 root root 11 Mar 9 20:07 .X0-lock
Line: 19: drwxrwxrwt 2 root root 4.0K Mar 9 20:07 .X11-unix
If you need more control and want also to read stderr, then you would have to create pipes for stdout and stderr,
make a fork and the child dup2 the pipes to stderr & stdout and
then execute exec (or any other function of that family) to execute the
program.
Like this:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
int stdout_pipe[2];
int stderr_pipe[2];
pipe(stdout_pipe);
pipe(stderr_pipe);
pid_t pid = fork();
if(pid < 0)
return 1;
if(pid == 0)
{
// closing reading ends and duplicating writing ends
close(stdout_pipe[0]);
dup2(stdout_pipe[1], STDOUT_FILENO);
close(stderr_pipe[0]);
dup2(stderr_pipe[1], STDERR_FILENO);
execlp("ls", "ls", "-alh", "a.c", "kslkdl", NULL);
exit(1);
}
// closing writing end
close(stdout_pipe[1]);
close(stderr_pipe[1]);
int status;
if(waitpid(pid, &status, 0) < 0)
{
fprintf(stderr, "could not wait\n");
return 1;
}
if(WIFEXITED(status) == 0)
{
fprintf(stderr, "ls exited abnormally\n");
close(stdout_pipe[0]);
close(stderr_pipe[0]);
return 1;
}
puts("STDOUT:");
char buffer[1024];
ssize_t len;
while((len = read(stdout_pipe[0], buffer, sizeof(buffer) - 1)) > 0)
{
buffer[len] = 0;
printf("%s", buffer);
}
putchar('\n');
close(stdout_pipe[0]);
puts("STDERR:");
while((len = read(stderr_pipe[0], buffer, sizeof(buffer) - 1)) > 0)
{
buffer[len] = 0;
printf("%s", buffer);
}
putchar('\n');
close(stderr_pipe[0]);
return 0;
}
which outputs:
$ ./b
STDOUT:
-rw-r--r-- 1 shaoran shaoran 3.6K Mar 9 22:29 a.c
STDERR:
ls: cannot access 'kslkdl': No such file or directory

Pablo's answer is correct, you need to use pipe(7)-s.
And you could probably use GTK & Glib's g_spawn_async_with_pipes (which is based on pipe and fork and execve on Linux) for that (instead of fork or popen). In a GTK interactive program, it is better than the usual popen because the forked program would run concurrently with your event loop.
You could even consider using g_source_add_unix_fd on the (or on some) of the pipe fd-s given by pipe(2) or by g_spawn_async_with_pipes which use that pipe(2) call. But you might prefer g_io_channel_unix_new and g_io_add_watch
Be aware that the GTK main loop (and Gtk Input and Event Handling Model), i.e. GtkApplication and the related g_application_run or the older gtk_main are an event loop around some multiplexing system call like poll(2) (or the older select(2)) and you probably need that loop to be aware of your pipes. When some data arrives on the pipe, you probably want to read(2) it (and then call some GtkTextBuffer insert function).
You should make design choices: do you want the GUI interface and the other process to run concurrently? Or is the other exe process always so quick and with a small output (and no input) that you might just use popen?
On current GUI applications, the event loop should run quickly (at least 30 or 50 times per second) if you want a responsive GUI app.
Look also for inspiration inside the source code of some existing free software GTK application (e.g. on github or from your Linux distro).

Related

File descriptors in linux; Pwnable kr challenge [duplicate]

This question already has answers here:
Writing to stdin and reading from stdout (UNIX/LINUX/C Programming)
(5 answers)
Closed 10 months ago.
In order to understand the workings of C and linux, I was doing the pwnable.kr challenges, but there is something in the first challenge that doesn't quite add to me.
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
if(argc<2){
printf("pass argv[1] a number\n");
return 0;
}
int fd = atoi( argv[1] ) - 0x1234;
int len = 0;
len = read(fd, buf, 32);
if(!strcmp("LETMEWIN\n", buf)){
printf("good job :)\n");
system("/bin/cat flag");
exit(0);
}
printf("learn about Linux file IO\n");
return 0;
}
To my understanding, the 0 fd is for stdin, and the 1 and 2 fd are for stdout, yet all three of them allow me to solve the challenge. I understand the first one, but wouldn't the other 2 file descriptors just read what I print on my promt, and not allow me to write anything after?
Let's take a look at what /proc says:
$ ls -al /proc/self/fd
total 0
dr-x------. 2 mrsam mrsam 0 Apr 23 15:25 .
dr-xr-xr-x. 9 mrsam mrsam 0 Apr 23 15:25 ..
lrwx------. 1 mrsam mrsam 64 Apr 23 15:25 0 -> /dev/pts/1
lrwx------. 1 mrsam mrsam 64 Apr 23 15:25 1 -> /dev/pts/1
lrwx------. 1 mrsam mrsam 64 Apr 23 15:25 2 -> /dev/pts/1
For an interactive terminal all three file descriptors are, basically the same. All three are connected to the terminal device, after all, why won't they be? Input and output goes to the same screen.
And, for simplicity, you can think of them as dup()s of each other. So, as a side effect, all three file descriptors are read/write.
Now, of course, this is only true for programs launched from an interactive terminal. In an arbitrary program, launched in an unknown environment you can only rely on being able to read from 0 and write to 1 and 2.

How to figure out if the current directory is the root in C?

I am currently doing an assignment where I need to implement the functionality of pwd without using getcwd or getenv. However, am stuck in an infinite loop because when I use chdir("..") it seems to route me back to the same directory once I reach the root. I found a similar question, but it was solved using a perl module so that didn't help.
My first thought was to keep track of the previous directory name and compare it with the current directory name to see if they matched, but I can't see any way to do that because I don't have the size of the directory name and there are null characters between the name of each file or directory in d_name.
Here is my code so far (note that it probably has other bugs as well, but I need to solve this first):
#include <stdio.h>
#include <dirent.h>
#include <linux/limits.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
void get_path(char *path, int path_len);
int main(void)
{
int path_len = -1;
char path[PATH_MAX];
/* get and print the path */
get_path(path, path_len);
path[0] = '\0';
printf("%s\n", path);
return 0;
}
/* get the path from the current directory to the root */
void get_path(char *path, int path_len)
{
struct dirent *dirp;
DIR *dp;
/* open current dir */
if ((dp = opendir(".")) == NULL)
{
perror("mypwd");
exit(1);
}
/* go through this dir */
while ((dirp = readdir(dp)) != NULL)
{
/* found its parent */
if (strcmp(dirp->d_name, "..") == 0)
{
/* on to a new path */
path_len++;
if (path_len > PATH_MAX)
{
fprintf(stderr, "path too long\n");
exit(1);
}
/* go to parent and continue */
chdir("..");
get_path(path, path_len);
/* add this name to our path */
strcat(path, dirp->d_name);
}
}
closedir(dp);
}
How can I figure out of I have reached the file root?
The inode number of the parent directory of the root directory is 2, the same as the inode number of the root directory itself (and the device numbers are the same). At least, this is true in classic Unix file systems — and some modern ones (probably all of them).
On my Mac, I can run:
$ ls -lid /. /..
2 drwxr-xr-x 34 root wheel 1088 Apr 25 07:55 /.
2 drwxr-xr-x 34 root wheel 1088 Apr 25 07:55 /..
$
Note that the root directory of a (mounted) file system has inode number 2 too on disk, but when you stat() the root directory of a mounted file system, you get a different inode and device number for the parent directory.
For example, on a Mac again, /private/var/vm/ is mounted:
$ ls -lid /private/var /private/var/vm /private/var/vm/. /private/var/vm/..
13212009 drwxr-xr-x 26 root wheel 832 Sep 26 2017 /private/var
2 drwxr-xr-x 6 root wheel 192 May 5 12:44 /private/var/vm
2 drwxr-xr-x 6 root wheel 192 May 5 12:44 /private/var/vm/.
13212009 drwxr-xr-x 26 root wheel 832 Sep 26 2017 /private/var/vm/..
$
And using a custom fstat program, you can see the device numbers changing:
$ fstat /private/var /private/var/vm /private/var/vm/. /private/var/vm/.. / /. /..
Mode Inode Links UID GID Size Modtime Dev RDev File
0040755 13212009 26 0 0 832 1506482024 16777223 0 /private/var
0040755 2 6 0 0 192 1525549483 16777221 0 /private/var/vm
0040755 2 6 0 0 192 1525549483 16777221 0 /private/var/vm/.
0040755 13212009 26 0 0 832 1506482024 16777223 0 /private/var/vm/..
0040755 2 34 0 0 1088 1524668138 16777223 0 /
0040755 2 34 0 0 1088 1524668138 16777223 0 /.
0040755 2 34 0 0 1088 1524668138 16777223 0 /..
$
GNU stat and BSD stat commands produce the same device number information, but their output is more verbose and/or more unreadable.

how many inodes, open file table entries and file descriptors this program use

I am reviewing some stuff for my job interview, on this question it asks at peak how many inodes, open file table entries and file descriptors this program use? Can you help me with it?
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char* argv[]){
char buffer[3] = "ab";
int r = open("new.txt", O_RDONLY);
int r1, r2, pid;
r1 = dup(r);
read(r, buffer, 1);
if((pid=fork())==0) {
r1 = open("new.txt", O_RDONLY);
} else{
waitpid(pid, NULL, 0);
}
read(r1, buffer+1, 1);
printf("%s", buffer);
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char* argv[]){
char buffer[3] = "ab";
int r = open("new.txt", O_RDONLY);
int r1, r2, pid;
r1 = dup(r);
read(r, buffer, 1);
if((pid=fork())==0) {
r1 = open("new.txt", O_RDONLY);
while (1)
{
sleep(1);
}
} else{
waitpid(pid, NULL, 0);
while (1)
{
sleep(1);
}
}
read(r1, buffer+1, 1);
printf("%s", buffer);
return 0;
}
wutiejun#linux-00343520:~/Temp> gcc -o test main.c
wutiejun#linux-00343520:~/Temp> ./test &
[1] 10404
wutiejun#linux-00343520:~/Temp> ls -l /proc/10404/fd
Total 0
lrwx------ 1 wutiejun users 64 June 9 16:33 0 -> /dev/pts/0
lrwx------ 1 wutiejun users 64 June 9 16:33 1 -> /dev/pts/0
lrwx------ 1 wutiejun users 64 June 9 16:33 2 -> /dev/pts/0
l-wx------ 1 wutiejun users 64 June 9 16:33 3 -> /home/wutiejun/Temp/new.txt
l-wx------ 1 wutiejun users 64 June 9 16:33 4 -> /home/wutiejun/Temp/new.txt
wutiejun#linux-00343520:~/Temp> ps -a
PID TTY TIME CMD
10404 pts/0 00:00:00 test
10405 pts/0 00:00:00 test
10417 pts/0 00:00:00 ps
wutiejun#linux-00343520:~/Temp> ls -l /proc/10405/fd
Total 0
lrwx------ 1 wutiejun users 64 June 9 16:34 0 -> /dev/pts/0
lrwx------ 1 wutiejun users 64 June 9 16:34 1 -> /dev/pts/0
lrwx------ 1 wutiejun users 64 June 9 16:33 2 -> /dev/pts/0
lr-x------ 1 wutiejun users 64 June 9 16:34 3 -> /home/wutiejun/Temp/new.txt
lr-x------ 1 wutiejun users 64 June 9 16:34 4 -> /home/wutiejun/Temp/new.txt
lr-x------ 1 wutiejun users 64 June 9 16:33 5 -> /home/wutiejun/Temp/new.txt
wutiejun#linux-00343520:~/Temp>
So, you can count the open files.
And I am not sure about the inode numbers, I think it depends on the different filesystems.
We have to make a few assumptions here.
Assumption 1: We are running on a system where all filesystems use inodes (it's just a name of a data structure. Inodes have not been exposed as an abstraction to processes for decades). A perfectly valid answer for the "number of inodes" question could be: 0 because I'm running on NFS. I would be much more comfortable if the term used was "vnode" because even though it's also a system specific term it is actually more meaningful in many more systems than "inode".
Assumption 2: the program starts with stdin/stderr/stdout open and pointing to the same file description.
Assumption 3: stdin&co are a pty from /dev
Assumption 4: /dev is a normal directory, as in classic unix and not like linux where it's a devfs. Also, the pty was opened as a normal file just like in the early 90s, not through some kind of cloning mechanism.
Assumption 5: "new.txt" exists and is accessible to this process.
Assumption 6: "file table entry" actually means file description.
Assumption 7: the program is not dynamically linked. This is because a dynamic linker could have opened an arbitrarily large number of files before the program reached main.
Assumption 8: stdio in libc doesn't open various locale and such databases until they are actually needed.
Assumption 9: things that can fail (fork, open, dup), don't.
Assumption 10: pid_t fits in an int.
Let's see what happens (the skipped lines are irrelevant to the question).
int main(int argc, char* argv[]){
At this point we have three open file descriptors. As in the assumptions, those file descriptors point to the same file description which comes from the same file on the filesystem.
Score (descriptors, descriptions, inodes): 3, 1, 1
int r = open("new.txt", O_RDONLY);
We successfully open a file.
Score: 4, 2, 2
r1 = dup(r);
We successfully dup the file descriptor. A dup:d file descriptor points to the same file description as the original descriptor.
Score: 5, 2, 2
if((pid=fork())==0) {
Fork copies the file descriptor table, but the descriptors still point to the same descriptions.
Score: 10, 2, 2
r1 = open("new.txt", O_RDONLY);
This creates a new file description that points to the same inode as we've already opened.
Score: 11, 3, 2
} else {
waitpid(pid, NULL, 0);
}
Let's assume that the child process has successfully exited despite doing it in a quite unclean way (forked children should exit through _exit, not by returning from main, but we'll let it slide this time). The child process closes all its descriptors, this also causes one description to be closed because it only had one reference.
Score: 5, 2, 2
Answer: Given all the assumptions, the peak was 11 file descriptors, 3 file descriptions ("file table entries" if we insist on using archaic terminology), 2 inodes.
Alternative answer: 11, 2 + X, 1 + Y, where X is the number of descriptions that stdin/out/err use and Y is the same for number of inodes.

NCURSES - missprinting new lines when printing in tty mode

I have this program:
#include <ncurses.h>
SCREEN * sstderr;
SCREEN * sstdout;
int main() {
sstderr = newterm(NULL, stderr, NULL);
noecho();
sstdout = newterm(NULL, stdout, stdin);
set_term(sstdout);
addstr("PRESS A KEY");
getch();
def_prog_mode();
endwin();
system("ls -l");
getchar();
reset_prog_mode();
refresh();
addstr("Press another key");
getch();
set_term(sstdout);
endwin();
set_term(sstderr);
endwin();
}
every line that is in the output of the 'ls -l' command gets missprinted like this:
drwxr-xr-x 2 root root 4096 Feb 11 09:22 bin
drwxr-xr-x 3 root root 4096 Mar 6 2016 boot
drwxr-xr-x 18 root root 3380 Feb 23 00:12 dev
drwxr-xr-x 113 root root 12288 Apr 25 10:45 etc
...
I tried using def_shell_mode() just before the initscr() line (or newterm() in my case), and reset_shell_mode() just before system("ls -l"); but the problem persists.
the only way I can kind of fix this is using
system("reset");
just before the system("ls -l"); line.
Anyone know what the real problem is, and how I can fix it without that "reset" call?
Thanks!
Your program initializes the same terminal into curses mode twice. But the first time it sets the terminal into raw mode starting from cooked mode. The second time it is already in raw mode. It doesn't matter (much) that those are separate streams, but that they're connected to the same terminal driver.
Having initialized the second screen (for standard output), and then doing a "restore", nothing happens because it restores to raw mode.
You could "fix" it by switching back to the standard-error screen before doing endwin. Offhand, you'd have problems copying the shell-mode terminal settings from one screen to another.

Process Hangs in Parent Process in C

I have a program that seems to be hanging in the parent process. It's a mock bash program that accepts commands like bash, and then operates them. The code is below. (Note this is simplified code without error checking so it would be more easily readable. Assume its all properly nested inside main function)
#define MAX_LINE 80
char *args[MAX_LINE/2 + 1];
while(should_run){
char *inputLine = malloc(MAX_LINE);
runConcurrently = 0; /*Resets the run in background to be line specific */
fprintf(stdout, "osh> "); /*Command prompt austhetic */
fflush(stdout);
/*User Input */
fgets(inputLine, MAX_LINE, stdin);
/*Reads into Args array */
char *token = strtok(inputLine, " \n");
int spot = 0;
while (token){
args[spot] = token;
token = strtok(NULL, " \n");
spot++;
}
args[spot] = NULL;
/* checks for & and changes flag */
if (strcmp(args[spot-1], "&") == 0){
runConcurrently = 1;
args[spot-1] = NULL;
}
/* Child-Parent Fork Process */
pid_t pid;
pid = fork(); /*Creates the fork */
if (pid == 0){
int run = execvp(args[0], args);
if (run < 0){
fprintf(stdout, "Commands Failed, check syntax!\n");
exit(1);
}
}
else if (pid > 0) {
if (!runConcurrently){
wait(NULL);
}
}
else {
fprintf(stderr, "Fork Failed \n");
return 1;
}
}
The problem here has to do with when I use an '&' and activate the run concurrently flag. This makes it so the parent no longer has to wait, however when I do this I lose some functionality.
Expected output:
osh> ls-a &
//Outputs a list of all in current directory
osh>
So I want it to run them concurently, but give control of the terminal back to me. But instead I get this.
Actual Result:
osh> ls -a &
//Outputs a list of all in current directory
<---- Starts a new line without the osh>. And stays like this indefinitely
And if I type something into this blank area the result is:
osh> ls -a &
//Outputs a list of all in current directory
ls -a
//Outputs a list of all in current directory
osh> osh> //I get two osh>'s this time.
This is my first time working with split processes and fork(). Am I missing something here? When I run it concurrently should I be choosing processes or something like that? Any help is welcome, thanks!
Your code actually works fine. The only problem is, you spit out the prompt "too quickly", and the new prompt appears before the command output. See a test output here:
osh> ls -al &
osh> total 1944 --- LOOK HERE
drwxrwxrwt 15 root root 4096 Feb 15 14:34 .
drwxr-xr-x 24 root root 4096 Feb 3 02:13 ..
drwx------ 2 test test 4096 Feb 15 09:30 .com.google.Chrome.5raKDW
drwx------ 2 test test 4096 Feb 15 13:35 .com.google.Chrome.ueibHT
drwx------ 2 test test 4096 Feb 14 12:15 .com.google.Chrome.ypZmNA
See the "LOOK HERE" line. The new prompt is there, but ls command output appears later. Your application is responsive to new commands, even though the prompt is displayed before command output. You can verify all this by using a command that does not produce any output, for example
osh> sleep 10 &
Hannu

Resources