Cloning command `script` and PTY background job problems: terminal messed up - c

I'm trying to recode the UNIX command script (as it is on OSX). This is part of an exercise for school to help students learn UNIX APIs. We are only allowed to use system calls, more specifically, only those available on MAN(2) pages on Mac OSX (since that's our OS at school).
I have a 'first version' that kind of works. Running a program such as ls prints the right output to the screen and in an output file.
The problem scenario
I run bash from within the script-clone. First issue is I get the following error:
bash: no job control in this shell
I have tried forcing the bash process into foreground with setpgrp and setpgid but that din't change anything so I concluded that was not the problem.
I also tried to understand why the real script command uses cfmakeraw (at least on Linux), as seen here, but I don't get it. The MAN page is not very helpful.
The real script also dup2s STDIN on the slave, as seen here, but when I do that, it seems like input isn't read anymore.
However, the bash still runs, and I can execute commands inside of it.
But if I run vim inside it, and then hit Ctrl-Z to put vim to the background, the terminal is messed up (which does not happen when I'm in my regular terminal).
So I guess I must have done something wrong. I'd appreciate any advice/help.
Here's the source code:
https://github.com/conradkleinespel/unix-command-script/tree/2587b07e7a36dc74bf6dff0e82c9fdd33cb40411
You can compile by doing: make (it builds on OSX 10.9, hopefully on Linux as well)
And run by doing: ./ft_script
Don't know it it makes more sense to have all the source code in StackOverflow as it would crowd the page with it. If needed, I can replace the Git link with the source.

I don't use OS X, so I can't directly test your code, but I'm currently writing a toy terminal emulator and had similar troubles.
about "bash: no job control in this shell"
In order to perform job control, a shell needs to be a session leader and the controlling process of its terminal. By default, your program inherits the controlling terminal of your own shell which runs your script program and which is also a session leader. Here is how to make your new slave process a session leader after fork:
/* we don't need the inherited master fd */
close(master);
/* discard the previous controlling tty */
ioctl(0, TIOCNOTTY, 0);
/* replace existing stdin/out/err with the slave pts */
dup2(slave, 0);
dup2(slave, 1);
dup2(slave, 2);
/* discard the extra file descriptor for the slave pts */
close(slave);
/* make the pts our controlling terminal */
ioctl(0, TIOCSCTTY, 0);
/* make a new session */
setsid()
At this point, the forked process has stdin/out/err bound to the new pts, the pts became its controlling terminal, and the process is a session leader. The job control should now work.
about raw tty
When you run a program inside a normal terminal, it looks like this:
(term emulator, master side) <=> /dev/pts/42 <=> (program, slave side)
If you press ^Z, the terminal emulator will write the ascii character 0x1A to the pts. It is a control character, so it won't be sent to the program, but instead the kernel will issue SIGSTP to the program and suspend it. The process of transforming characters into something else is called "line cooking" and has various settings that can be adjusted for each tty.
Now let's look at the situation with script:
term emulator <=> /dev/pts/42 <=> script <=> /dev/pts/43 <=> program
With normal line settings, what happens when you press ^Z? It will be transformed into SIGSTP by /dev/pts/42 and script will be suspended. But that's not what we want, instead we'd like the 0x1A character produced by our ^Z to go as-is through /dev/pts/42, then be passed by script to /dev/pts/43 and only then be transformed into SIGSTP to suspend the program.
This is the reason why the pts between your terminal and script must be configured as "raw", so that all control characters reach the pts between script and the program, as if you were directly working with it.

Related

How to create an attachable CLI (terminal) for daemon?

Problem
I would like to program an attachable command line interface for my daemon.
I developped a daemon running 24/7 on Linux Openwrt:
#!/bin/sh /etc/rc.common
START=98
USE_PROCD=1
PROCD_DEBUG=1
start_service() {
procd_open_instance
procd_set_param command "/myProgram"
procd_set_param respawn
procd_close_instance
}
I would like to add a debug user interfaces for test. So we could live tune some parameters/actions and print log. Something like the screen package.
Hence i want to create a command line interface for this daemon.
Research
Stdin/Stdout
Ideally i would like to write directly to the stdin of the daemon and read the stdout.
Daemon
Duplicate stdin to a file.
Duplicate stoud to a file.
Client
A launched C program by the tester.
It would relay stdin to stdinfile of daemon and stdoutfile of daemon to stdout.
Critic
That would be maybe the simplest way and I could read stdout.
I couldn't find any exemples, it makes me think i'm overlooking something.
Theres a risk I fill the flash by writing endlessly to the stdoutfile.
Pipes
The creation of 2 named pipe can be possible.
Daemon
The daemon would create a named input pipe and poll the pipe by making non blocking read.
A second output pipe is necessary to write the return of the command received.
Client
A launched C program by the tester.
It would relay stdin to input pipe and output pipe to stdout.
Critic
I don't know if I can properly redirect the stdout of the daemon to output pipe. Which means I wont be able to print the stdout logs but only specific cli coded response.
MessageQ
Same issues as pipe.
Sockets
Seems rather complex for a simple application.
Shared Memory
The paradigm does not seems appropriate.
Pty
Maybe something can be done with pseudo terminals but I don't understand them even after reading explanations: attach a terminal to a process running as a daemon (to run an ncurses UI)
Screen/Tmux
I don't have screen or tmux in my repository.
Question
What is the proper way to create a CLI for a daemon ? Where could I find an exemple ?
I would use a Unix domain stream socket, with the CLI thread in a blocking accept() until a connection is obtained.
These sockets are bidirectional, and you can write a trivial CLI application to read from standard input to the connected socket, and from the connected socket to standard output. (That same trivial CLI program could be used to redirect the output over e.g. SSH to ones local computer with much more storage, running the CLI program remotely using something like ssh -l username openwrt-device.name-or-address cli-program | tee local-logfile. OpenWrt devices often don't have suitable storage for log files, so this can be very useful.)
Use vdprintf() to implement your own printf() that writes to the connected CLI.
Because sockets are bidirectional, if you want to use locking –– for example, to avoid mixing logging output and CLI responses ––, use a mutex for writing; the read side does not need to take the mutex at all.
You cannot really use <stdio.h> FILE * stream handles for this, because its internal buffering can yield unexpected results.
Assuming your service daemon uses sockets or files, it can be very useful to reserve the file descriptor used for the bidirectional CLI connection, by initially opening /dev/null read-write (O_RDWR). Then, when the connection is accept()ed, use dup2() to move the accepted connection descriptor to the reserved one. When the connection is to be closed, use shutdown(fd, SHUT_RDWR) first, then open /dev/null, and dup that descriptor over the connection to be closed. This causes the connection to be closed and the descriptor to be reopened to /dev/null, in an atomic manner: the descriptor is never "unused" in between. (If it is ever close()d in a normal manner, another thread opening a file or socket or accepting a new connection may reuse that descriptor, causing all sorts of odd effects.)
Finally, consider using an internal (cyclic) buffer to store the most recent output messages. You do not need to use a human-readable format, you can use e.g. the first character (codes 1 to 254) to encode the severity or log level, keeping NUL (0) as the end-of-string mark, and 255 as the "CLI response" mark, so that your CLI program can use e.g. ANSI colors to color the output if output is a terminal. (For example, "\033[1;31m" changes output to bright red, and "\033[0m" returns the output back to normal/default. The \033 refers to a single character, code 27, ASCII ESC.) This can be very useful to efficiently indicate the priority/severity of each separate output chunk to the human user. The Linux kernel uses a very similar method in its kernel logging facility.

Disallowing printf in child process

I've got a cmd line app in C under Linux that has to run another process, the problem is that the child process prints a lot in a comand line and the whole app gets messy.
Is it possible to disallow child process to print anything in cmd line from parent process? It would be very helpful to for example being able to define a command that allows or disallows printing by a child process.
There's the time-honoured tradition of just redirecting the output to the bit bucket(a), along the lines of:
system("runChild >/dev/null 2>&1");
Or, if you're doing it via fork/exec, simply redirect the file handles using dup2 between the fork and exec.
It won't stop a determined child from outputting to your standard output but it will have to be very tricky to do that.
(a) I'm not usually a big fan of that, just in case something goes wrong. I'd prefer to redirect it to a real file which can be examined later if need be (and deleted eventually if not).
Read Advanced Linux Programming then syscalls(2).
On recent Linux, every executable is in ELF format (except init or systemd; play with pstree(1) or proc(5)) is running in a process started by fork(2) (or clone(2)...) and execve(2).
You might use cleverly dup2(2) with open(2) to redirect STDOUT_FILENO to /dev/null (see null(4), stdout(3), fileno(3))
I've got a cmd line app in C under Linux that has to run another process, the problem is that the child process prints a lot in a comand line
I would instead provide a way to selectively redirect the child process' output. You could use program arguments or environment variables (see getenv(3) and/or environ(7)) to provide such an option to your user.
An example of such a command program starting and redirecting subprocesses and redirecting them is your GCC compiler (see gcc(1); it runs cc1 and as(1) and ld(1)...). Consider downloading and studying its source code.
Study also -for inspiration- the source code of some shell (e.g. sash), or write your own one.

Writing a C program to move a process to background

I am trying to write a program , which does a fork and exec a child process and executes it in the back ground .
One approach I would see is to redirect the output to /dev/NULL file and come back to my main program . Any other ideas ?
After a process is started, shell has no more control on process file descriptors so you can not silence it by a shell command i.e. terminal has its stdin, stdout and stderr bound to the terminal and you cannot do anything about it without re-gaining control over that terminal.
There is a tool called retty how you can use it can be seen at this link retty this tool is used to attach processes running on terminals
Beside you can also use the built in disown command to disown the process which will prevent from sending a SIGHUP signal to the program when the shell exits
This link can be helpful Link to a similar problem

Less Hacky Way Than Using System() Call?

So I have this old, nasty piece of C code that I inherited on this project from a software engineer that has moved on to greener pastures. The good news is... IT RUNS! Even better news is that it appears to be bug free.
The problem is that it was designed to run on a server with a set of start up parameters input on the command line. Now, there is a NEW requirement that this server is reconfigurable (didn't see that one coming...). Basically, if the server receives a command over UDP, it either starts this program, stops it, or restarts it with new start up parameters passed in via the UDP port.
Basically the code that I'm considering using to run the obfuscated program is something like this (sorry I don't have the actual source in front of me, it's 12:48AM and I can't sleep, so I hope the pseudo-code below will suffice):
//my "bad_process_manager"
int manage_process_of_doom() {
while(true) {
if (socket_has_received_data) {
int return_val = ParsePacket(packet_buffer);
// if statement ordering is just for demonstration, the real one isn't as ugly...
if (packet indicates shutdown) {
system("killall bad_process"); // process name is totally unique so I'm good?
} else if (packet indicates restart) {
system("killall bad_process"); // stop old configuration
// start with new parameters that were from UDP packet...
system("./my_bad_process -a new_param1 -b new_param2 &");
} else { // just start
system("./my_bad_process -a new_param1 -b new_param2 &");
}
}
}
So as a result of the system() calls that I have to make, I'm wondering if there's a neater way of doing so without all the system() calls. I want to make sure that I've exhausted all possible options without having to crack open the C file. I'm afraid that actually manipulating all these values on the fly would result in having to rewrite the whole file I've inherited since it was never designed to be configurable while the program is running.
Also, in terms of starting the process, am I correct to assume that throwing the "&" in the system() call will return immediately, just like I would get control of the terminal back if I ran that line from the command line? Finally, is there a way to ensure that stderr (and maybe even stdout) gets printed to the same terminal screen that the "manager" is running on?
Thanks in advance for your help.
What you need from the server:
Ideally your server process that you're controlling should be creating some sort of PID file. Also ideally, this server process should hold an exclusive lock on the PID file as long as it is still running. This allows us to know if the PID file is still valid or the server has died.
Receive shutdown message:
Try to get a lock on the PID file, if it succeeds, you have nothing to kill (the server has died, if you proceed to the kill regardless, you may kill the wrong process), just remove the old PID file.
If the lock fails, read the PID file and do a kill() on the PID, remove the old PID file.
Receive start message:
You'll need to fork() a new process, then choose your flavor of exec() to start the new server process. The server itself should of course recreate its PID file and take a lock on it.
Receive restart message:
Same as Shutdown followed by Start.

Make C Program for Interactive Mode

Here I have one command which is like interactive mode:
obex_test -b $BD_ADDR $CH_NUM
This command is from a script but I want to run this command through a system call in a C program.
obex_test is nothing but obex file transfer library.
Here I want to receive a file from remote device to local device through bluetooth.
This is the manual page of obex_test
Please can anybody tell me how can I put my C program in interactive mode like this command, and I want to use this command also.
I used popen(command,"r") but its not useful; it does not take input from the user.
If I used "w" mode then I don't know what happens; I directly get a message like >Unknown Command. It's the error this command gives when we give different options. So it's taken something as a write mode.
You could have two pairs of pipes (created with the pipe(2) system call); one for data from your program to obex_test's stdin and one from obex_test's stdout to your program. Then you would fork and execve... Beware of deadlocks (your program blocked on writing to obex_test stdin when its output pipe is full and blocking it), you might need to call poll(2) or select(2)...
However, as it man pages explain, "obex_test is a test application for the libopenobex library". So why don't call directly functions inside this libopenobex library, which you would link to your program?
You can use the system command. Check the manual page for more details.
For e.g. system( "obex_test -b 172.16.7.1 1234" );

Resources