I am working on a pseudo terminal library. The code is implemented in C code and the code is used by a web based terminal. The code works as long as I do not use sudo or login.
This is the error I get when I run the server on a Mac:
sh-3.2$ sudo ls
Password:
[1]+ Stopped(SIGTTOU)
sh-3.2$
The above works on Linux:
$ sudo ls
readme.txt
However, I get the following on Linux with sudo bash:
$ sudo bash
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
]0;root#ubuntu: /tmproot#ubuntu:/tmp#
Note: the above works, but I have no job control.
I have probably forgot to set some controlling bits on the terminal, but Google has not been very helpful in finding this.
Also, do you know of any good books that explains Pseudo terminal management in great detail.
I have the setsid call, but I am not using openpty. I use the following code when opening the pty:
static int createPty(lua_State* L, char* ttyName, int* pty)
{
*pty = getpt();
if (*pty < 0 || grantpt(*pty) < 0 || unlockpt(*pty) < 0)
return lDoErr(L,"Cannot open PTY: %s",strerror(errno));
if(ptsname_r(*pty, ttyName, PTY_NAME_SIZE-1))
return lDoErr(L,"ptsname_r: %s",strerror(errno));
return 0;
}
I have edited the code below and this code works. The reason my first version did not work was that I tried to create two PTY channels. I wanted to be able to differentiate between stdout and stderr, but the Linux kernel does not allow multiple TIOCSCTTY calls.
static int
childOpenTTY(const char* ttyName)
{
struct termios termbuf;
int fd=open(ttyName, O_RDWR);
if(fd < 0)
doClientError("open %s: %s",ttyName, strerror(errno));
tcsetpgrp(fd, getpid());
ioctl(fd,TIOCSCTTY,NULL);
tcgetattr(fd, &termbuf);
cfmakeraw(&termbuf); /* turn off NL to CR/NL mapping on output. */
tcsetattr(fd, TCSANOW, &termbuf);
return fd;
}
if( (ret = createPty(L, ttyName, &te->pty)) != 0)
return ret;
if ((te->pid = zzbafork()) < 0)
return lDoErr(L,"fork: %s",strerror(errno));
if(te->pid == 0)
{ /* Child process */
static const char efmt[]={"Cannot set '%s' (dup2 err)"};
int fd;
if(setsid() < 0) /* make new process group */
doClientError("setsid: %s",strerror(errno));
fd=childOpenTTY(ttyName);
if(dup2(fd, STDIN_FILENO) != STDIN_FILENO)
doClientError(efmt,"stdin");
if(dup2(fd, STDOUT_FILENO) != STDOUT_FILENO)
doClientError(efmt,"stdout");
if(dup2(fd, STDERR_FILENO) != STDERR_FILENO)
doClientError(efmt,"stderr");
if(fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO)
close(fd);
execve(cmd, (char**)cmdArgv, environ);
/* execve should not return, unless error exec cmd */
doClientError("Executing %s failed: %s",cmd,strerror(errno));
}
It's hard to be sure since there's no actual code shown here, but I suspect you're running into POSIX-style "session" management. You need to execute a setsid call, then open the pty (slave side) such that it becomes the controlling terminal. The openpty and login_tty routines do the low level grunge work for you; are you using those?
Related
I'm using Libssh to log into an ethernet switch and run commands. I can connect and log in just fine but commands cannot be run. The program just returns "Channel request exec failed" as the ssh error. Normal ssh outside of the program works as expected and commands can be executed. The program can run commands on my localhost just fine. I've tried different commands as well, including just the help command or things that would never produce errors if run. This is the function I'm using to send the command:
int send_command(ssh_session session, char *command){
int rc;
ssh_channel channel;
channel = ssh_channel_new(session);
if (channel == NULL){
fprintf(stderr, "***Error in channel creation: %s***\n", ssh_get_error(session));
exit(-1);
}
rc = ssh_channel_open_session(channel);
if (rc != SSH_OK){
fprintf(stderr, "***Error opening channel: %s***\n", ssh_get_error(session));
exit(-1);
}
rc = ssh_channel_request_exec(channel, command);
if (rc != SSH_OK){
fprintf(stderr, "***Error sending command: %d, %s***\n", rc, ssh_get_error(session));
exit(-1);
}
char buffer[256];
int nbytes;
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer),0);
while (nbytes > 0){
if (fwrite(buffer, 1, nbytes, stdout) != nbytes){
fprintf(stderr,"***Error writing result: %s***\n", ssh_get_error(session));
exit(-1);
}
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
}
ssh_channel_send_eof(channel);
ssh_channel_close(channel);
ssh_channel_free(channel);
return SSH_OK;
}
I think it might have something to do with the custom CLI the switch is using - it's not a true linux terminal. The switch processor I'm logging into is running some kind of simplistic linux OS that I can't find information on. Are there different types of consoles I need to account for? Should I be opening a shell, even though the commands are programmatic and won't require user input? I've mostly been following the libssh tutorial to get this working so I don't know much about why things work or don't work, just that they do. I'm running this from a Cygwin enviroment on a windows machine if that matters.
i am writing a driver code, to read some register values from x86., when i ran my user space application i got the below error.
ioctl:Inappropriate ioctl for device
here is the code sniff..
fd = open_drvfile();
if(ioctl(fd, IOCTL_MSR_CMDS, (long long)msr_start) == -1 ) {
perror("ioctl:");
exit (0);
}
and open_drvfile() just open(create and open) the char file as below
fd = open("/dev/" DEV_NAME, O_RDWR|O_CREAT);
if (fd == -1) {
perror("Failed to open /dev/" DEV_NAME);
}
return fd;
can some one point where i made mistake on this?
A char device implies that it shall be created with mknod(), and not with O_CREAT under open() flags (which will create a regular file, not a char device).
(see question comments).
close(fileno(stdout));
int fd = dup(fileno(stdin));
//printf("Hello World\n");
write(fd, "Hello", 7);
Here for both printf and write is writing Hello to the screen..But I think they should not because I am duplicating the stdin to 1 or stdout closed perviously. And printf is connected to stdout but not stdin but still they are printing...Please explain if I have anything wrong in understanding the dup
The printf and write in your code happen to work, due to historical custom. Here's what's happening:
When a user logs into a terminal on a Unix-like system, or opens a terminal window under X11, file descriptors 0, 1, and 2 are connected to a terminal device, and each of them is opened for both reading and writing. This is the case despite the fact that one normally only reads from fd 0 and writes to fd 1 and 2.
Yet here is the code from 7th edition init.c:
open(tty, 2);
dup(0);
dup(0);
...
execl(getty, minus, tty, (char *)0);
And here is how ssh does it:
ioctl(*ttyfd, TCSETCTTY, NULL);
fd = open("/dev/tty", O_RDWR);
if (fd < 0)
error("%.100s: %.100s", tty, strerror(errno));
close(*ttyfd);
*ttyfd = fd;
...
/* Redirect stdin/stdout/stderr from the pseudo tty. */
if (dup2(ttyfd, 0) < 0)
error("dup2 stdin: %s", strerror(errno));
if (dup2(ttyfd, 1) < 0)
error("dup2 stdout: %s", strerror(errno));
if (dup2(ttyfd, 2) < 0)
error("dup2 stderr: %s", strerror(errno));
(The dup2 function dups arg1 into arg2, closing arg2 first if necessary.)
And here is how xterm does it:
if ((ttyfd = open(ttydev, O_RDWR)) >= 0) {
/* make /dev/tty work */
ioctl(ttyfd, TCSETCTTY, 0);
...
/* this is the time to go and set up stdin, out, and err
*/
{
/* dup the tty */
for (i = 0; i <= 2; i++)
if (i != ttyfd) {
IGNORE_RC(close(i));
IGNORE_RC(dup(ttyfd));
}
/* and close the tty */
if (ttyfd > 2)
close_fd(ttyfd);
Back to your code.
close(fileno(stdout));
This closes fd 1.
int fd = dup(fileno(stdin));
This duplicates fd 0 into the lowest available fd, which is 1, and assigns 1 to fd. (This assumes that fd 0 is open, of course.) Both fd's 0 and 1 are now open for reading and writing (assuming they're connected to a terminal device). On a Linux system you can verify this:
$ cat /proc/self/fdinfo/0
pos: 0
flags: 0100002
mnt_id: 20
$ cat /proc/self/fdinfo/1
pos: 0
flags: 0100002
mnt_id: 20
The 2 in flags, which is the constant O_RDWR, means open for reading and writing.
printf("Hello World\n");
This writes to fd 1, which is, once again, open for reading and writing to your terminal.
write(fd, "Hello", 7);
This writes to fd 1 again. (Note that "Hello" is only 6 bytes, though.)
If you are running your program on a terminal, then stdin and stdout are opens of the same file - your terminal device. So it should not be surprising that writing to stdin - or a dup() of fileno(stdin) - writes to your terminal device, and you see it.
If you run your program with stdin connected somewhere else, like a disk file, you'll see a different result:
./program < file
I wrote a program to get the details of hard disk drive using HDIO_ ioctl calls.
For writing program, I'm referring Documentation/ioctl/hdio.txt in kernel source(2.6.32).
Here is my main part of code:
unsigned char driveid[512];
fd = open("/dev/sda", O_RDONLY); // validated fd.
retval = ioctl(fd, HDIO_GET_IDENTITY, &driveid);
if(retval < 0) {
perror("ioctl(HDIO_GET_IDENTITY)");
exit(3);
}
When I run(as root) the above code, I got below error:
ioctl(HDIO_GET_IDENTITY): Invalid argument
What is the wrong in the program?
Why I'm getting error?
Additional Info: OS: CentOS-6.5, kernel version: 2.6.32, IA:x86_64 (running on VMware).
the HDIO_GET_IDENTITY ioctl() doesn`t take a raw character buffer as its 3rd argument.
it uses a struct defined in linux/hdreg.h.
struct hd_driveid driveid;
fd = open("/dev/sda", O_RDONLY); // validated fd.
retval = ioctl(fd, HDIO_GET_IDENTITY, &driveid);
if(retval < 0) {
perror("ioctl(HDIO_GET_IDENTITY)");
exit(3);
}
this way it should work. Be aware that it only works for IDE/SATA drives, SCSI is not supported.
has
if you are wondering on how to get the information after the command ioctl() has returned succesfully, I suggest going through
http://lxr.free-electrons.com/source/include/linux/hdreg.h?v=2.6.36
In C pseudo-code:
while (1) {
fifo = open("fifo", O_RDONLY | O_NONBLOCK);
fd_set read;
FD_SET(fifo, &read);
select(nfds, &read, NULL, NULL, NULL);
}
The process sleeps as triggered by select() until another process writes into fifo. Afterwards it will always find fifo as a readable file descriptor.
How to avoid this behavior (that is, after fifo has been read once, how to make it be found as unreadable until it gets another write?)
You opened that FIFO as read only (O_RDONLY), whenever there is no writer to the FIFO, the read end will receive an EOF.
Select system call will return on EOF and for every EOF you handle there will be a new EOF. This is the reason for the observed behavior.
To avoid this open that FIFO for both reading and writing (O_RDWR). This ensures that you have at least one writer on the FIFO thus there wont be an EOF and as a result select won't return unless someone writes to that FIFO.
The simple answer is to read until read() returns EWOULDBLOCK (or EAGAIN), or craps out with an error.
What you are saying simply cannot be happening unless the operating system (or runtime) that you are using is buggy. Otherwise you must be doing something wrong. For example, select() is using level-triggered I/O. I'd think that, most likely, you are not draining the socket completely, and so select() always indicates that you have something left in there (this does not happen with edge-triggered event notifications).
Below is a simple example that shows how one should read until the read() returns EWOULDBLOCK in order to avoid leaving descriptor in readable state (I've compiled and tested this on OS X, and there is also mostly no error checking, but you should get the idea):
/*
* FIFO example using select.
*
* $ mkfifo /tmp/fifo
* $ clang -Wall -o test ./test.c
* $ ./test &
* $ echo 'hello' > /tmp/fifo
* $ echo 'hello world' > /tmp/fifo
* $ killall test
*/
#include <sys/types.h>
#include <sys/select.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd;
int n;
fd_set set;
ssize_t bytes;
size_t total_bytes;
char buf[1024];
fd = open("/tmp/fifo", O_RDWR | O_NONBLOCK);
if (fd == -1) {
perror("open");
return EXIT_FAILURE;
}
FD_ZERO(&set);
FD_SET(fd, &set);
for (;;) {
n = select(fd+1, &set, NULL, NULL, NULL);
if (!n)
continue;
if (n == -1) {
perror("select");
return EXIT_FAILURE;
}
if (FD_ISSET(fd, &set)) {
printf("Descriptor %d is ready.\n", fd);
total_bytes = 0;
for (;;) {
bytes = read(fd, buf, sizeof(buf));
if (bytes > 0) {
total_bytes += (size_t)bytes;
} else {
if (errno == EWOULDBLOCK) {
/* Done reading */
printf("done reading (%lu bytes)\n", total_bytes);
break;
} else {
perror("read");
return EXIT_FAILURE;
}
}
}
}
}
return EXIT_SUCCESS;
}
Basically, level-triggered I/O means that you get notified all the time if there is something to read, even though you might have been notified of this before. On a contrary, edge-triggered I/O means that you are getting notified only once every time new data arrives and it doesn't matter whether you read it or not. select() is a level-triggered I/O interface.
Hope it helps. Good Luck!