I have written a program (with code from SO) that does printenv | sort | less and now I should implement error-handling. How can that be done? The program should fail gracefully, for example when passed the wrong arguments.
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
struct command
{
const char **argv;
};
/* Helper function that spawns processes */
int spawn_proc (int in, int out, struct command *cmd) {
pid_t pid;
if ((pid = fork ()) == 0) {
if (in != 0) {
dup2 (in, 0);
close (in);
}
if (out != 1) {
dup2 (out, 1);
close (out);
}
return execvp (cmd->argv [0], (char * const *)cmd->argv);
}
return pid;
}
/* Helper function that forks pipes */
int fork_pipes (int n, struct command *cmd) {
int i;
int in, fd [2];
for (i = 0; i < n - 1; ++i) {
pipe (fd);
spawn_proc (in, fd [1], cmd + i);
close (fd [1]);
in = fd [0];
}
dup2 (in, 0);
return execvp (cmd [i].argv [0], (char * const *)cmd [i].argv);
}
int main (int argc, char ** argv) {
int i;
if (argc == 1) { /* There were no arguments */
const char *printenv[] = { "printenv", 0};
const char *sort[] = { "sort", 0 };
const char *less[] = { "less", 0 };
struct command cmd [] = { {printenv}, {sort}, {less} };
return fork_pipes (3, cmd);
}
if (argc > 1) { /* I'd like an argument */
if (strncmp(argv[1], "cd", 2) && strncmp(argv[1], "exit", 2)) {
char *tmp;
int len = 1;
for( i=1; i<argc; i++)
{
len += strlen(argv[i]) + 2;
}
tmp = (char*) malloc(len);
tmp[0] = '\0';
int pos = 0;
for( i=1; i<argc; i++)
{
pos += sprintf(tmp+pos, "%s%s", (i==1?"":"|"), argv[i]);
}
const char *printenv[] = { "printenv", 0};
const char *grep[] = { "grep", "-E", tmp, NULL};
const char *sort[] = { "sort", 0 };
const char *less[] = { "less", 0 };
struct command cmd [] = { {printenv}, {grep}, {sort}, {less} };
return fork_pipes (4, cmd);
free(tmp);
} else if (! strncmp(argv[1], "cd", 2)) { /* change directory */
printf("change directory to %s\n" , argv[2]);
chdir(argv[2]);
} else if (! strncmp(argv[1], "exit", 2)) { /* change directory */
printf("exit\n");
exit(0);
}
}
exit(0);
}
It's going to be frankly a bit painful to go through your program and fix all those missing-error-handling bugs after the fact. Much better would have been to write correct code from the start! Moreover, you have more bugs than just missing error handling. I didn't scan all of your code, but at first glance I already saw one use of an uninitialized local variable (in in fork_pipes is used before it is set). Any decent compiler with warnings enabled would have caught that.
As a direct answer to your question, you'll just have to go through and spot every system call or library function call that is capable of returning errors, see if you are checking for them, and add checks if they are not already there. fork, malloc, dup2 — everything.
Related
I am learning how to write Linux Device Driver.
I wrote a dummy character device driver, implemented open, release, write, read in fops;
When I read from device , everything was ok;
When I wrote to device by "echo xx > ", the OS was hang.
Even I comment out all codes in write function except pr_alert and return statements, It still hangs;
Could anybody help me figure it out?
#include <linux/init.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/string.h>
struct hello_dev
{
char *buffer;
int length;
dev_t dev;
struct mutex lock;
struct cdev *pcdev;
};
struct hello_dev *pHelloDev;
int open_device(struct inode *pinode, struct file *filp)
{
filp->private_data = pHelloDev;
return 0;
}
int close_device(struct inode *pinode, struct file *filp)
{
struct hello_dev *pDev = filp->private_data;
if (pDev->buffer != NULL)
kfree(pDev->buffer);
pDev->buffer = NULL;
return 0;
}
ssize_t read_device(struct file *filp, char __user *buffer, size_t len, loff_t *loff)
{
pr_alert("read\n");
struct hello_dev *pDev = filp->private_data;
mutex_lock(&pDev->lock);
if (pDev->buffer == NULL)
{
mutex_unlock(&pDev->lock);
return 0;
}
int length = strlen(pDev->buffer);
// offset max than strlen in buffer, return
if (*loff > (length - 1))
{
mutex_unlock(&pDev->lock);
return 0;
} else {
// available to read
int len2read = length - *loff;
if (len < len2read)
{// buffer length less than available data
len2read = len;
}
int read = copy_to_user(buffer, pDev->buffer + *loff, len2read);
if (read)
{
*loff = *loff + read;
mutex_unlock(&pDev->lock);
return read;
} else {
*loff = *loff + len2read;
mutex_unlock(&pDev->lock);
return len2read;
}
}
}
ssize_t write_device(struct file *filp , const char __user *buffer, size_t len, loff_t* loff) {
pr_alert("write %s\n", buffer);
// struct hello_dev *pDev = filp->private_data;
// mutex_lock(&pDev->lock);
// if(pDev->buffer == NULL) {
// pDev->buffer = kmalloc(100, GFP_KERNEL);
// pDev->length = 100;
// }
// copy_from_user(pDev->buffer, buffer, len);
// *loff = *loff + len;
// mutex_unlock(&pDev->lock);
return len;
}
struct file_operations fops = {
.open = open_device,
.release = close_device,
.read = read_device,
.write = write_device
};
int init_device(void)
{
pr_alert("init device\n");
pHelloDev = kmalloc(sizeof(struct hello_dev), GFP_KERNEL);
pHelloDev->buffer = NULL;
pHelloDev->length = 0;
int ret = alloc_chrdev_region(&pHelloDev->dev, 0, 1, "hello");
if (ret)
goto alloc_error;
if (pHelloDev == NULL)
goto kmalloc_error;
pHelloDev->pcdev = cdev_alloc();
pHelloDev->pcdev->ops = &fops;
mutex_init(&pHelloDev->lock);
ret = cdev_add(pHelloDev->pcdev, pHelloDev->dev, 1);
if (ret)
goto cdev_add_error;
return 0;
alloc_error:
pr_alert("alloc_chrdev_region error, %d\n", ret);
return ret;
kmalloc_error:
pr_alert("alloc struct hello_dev error");
return -ENOMEM;
cdev_add_error:
pr_alert("cdev_add error, %d\n", ret);
return ret;
}
void cleanup_device(void)
{
pr_alert("unload ko\n");
cdev_del(pHelloDev->pcdev);
unregister_chrdev_region(pHelloDev->dev, 1);
}
MODULE_LICENSE("GPL");
module_init(init_device);
module_exit(cleanup_device);
I found why write to device hangs.
//this statements has problem
//maybe there is no \0 in buffer
//so I print it out, it will hang
//I wrote a program to write something to device
//and used strace to trace system call made by this program
//and found it hangs at write(...) system call
//and there was nothing printed out
//so, it must be this statement causing the problem
//when I removed this statement, everything was ok
pr_alert("write %s\n", buffer);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
char** get_env(void){
char *ptr, *ch, *next_ptr;
char **tok = malloc(sizeof(char*) * 20);
int i = 0;
ch = getenv("PATH");
for(i=0, ptr=ch; ;ptr=NULL,i++){
tok[i] = strtok_r(ptr, ":", &next_ptr);
if(tok[i] == NULL) break;
}
tok[i] = NULL;
return tok;
}
char **get_input(char *input){
char **command = malloc(8 * sizeof(char *));
char *separator = " ";
char *parsed, *nextptr;
int index = 0;
parsed = strtok_r(input, separator, &nextptr);
while(parsed != NULL){
command[index] = parsed;
index++;
parsed = strtok_r(NULL, separator, &nextptr);
}
command[index] = NULL;
return command;
}
int main(void) {
char **env = get_env();
pid_t pid;
int stat_loc;
while(1) {
int i = 0;
char *nextptr, *parsed;
char *command[20];
char* input = malloc(sizeof(char)*20);
printf("$>");
input = fgets(input,19,stdin);
input[strlen(input) - 1] = '\0';
if(strcmp("exit",input) == 0) {
free(input);
break;
}
parsed = strtok_r(input, " ",&nextptr);
while(parsed) {
command[i] = parsed;
printf("%s \n", command[i]);
i++;
parsed = strtok_r(NULL, " ", &nextptr);
}
command[i] = NULL;
pid = fork();
if(pid == 0){
if(execvp(command[0], command) == -1) {
printf("FAILED \n");
exit(0);
}
}
if(pid > 0){
wait(&stat_loc);
}
free(input);
}
free(env);
return 0;
}
I am trying to write a simple linux shell program in C. execvp function is being used.
when command[0](char *) and command(char * []) is entered to execvp, return value is only -1, it doesn't show running program such as pwd, ls.
I searched several blogs and manuals in web, but i can't find how to solve this error.
How can i make execvp function work?
---edited---
when i input ls -a in char* input variable, strtok_r function devide the string to pointer array(char * command[]), and command(command[0]) and argument(command) is entered execvp function.
i want to execute appropriate program such as cd, pwd, ls. But i cannot see program running, "FAILED" is only shown.
I have a problem with my code. I want to make communication between 2 children process. One of them is a server, which opens a file and sends each letter to the second process. The second process is counting letters and it should make a new file and save results. I have problems with the last step because the first process gonna finish faster than the second, what causes the end of the program. I have no idea how fix it. Looking for some tips :).
Here you got result.
My code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
//stale
#define FIFO "my_fifo"
#define SIZE 26
//zmienne globalne
int desk; //deskryptor pliku
int tab[SIZE];
//prototypy funkcji
void parentKillAll();
void server(FILE * file);
void client();
void cleanUp(FILE * file);
int checkEntryData(int argc, char *argv);
void replaceTabWithZero(int * tab);
void countLetters(int * tab, char ch);
void saveResults(int * tab, char *title);
void showTab(int * tab);
int main(int argc, char *argv[]) {
if (!checkEntryData(argc, argv[1]))
return 1;
replaceTabWithZero(tab);
FILE *file = fopen(argv[1], "r");
umask(0);
mkfifo(FIFO, 0666);
if (file) {
if (fork() == 0) {
server(file);
exit(0);
} else if (fork() == 0) {
client();
saveResults(tab, strcat(argv[1], "Result"));
showTab(tab);
exit(0);
} else {
cleanUp(file);
parentKillAll();
}
} else {
perror("Error");
}
return 0;
}
void parentKillAll() {
sleep(1);
kill(0, SIGKILL);
exit(0);
}
void server(FILE * file) {
char ch;
while ((ch = fgetc(file)) != EOF) {
desk = open(FIFO, O_WRONLY);
write(desk, &ch, 1);
}
}
void client() {
char ch;
while (1) {
desk = open(FIFO, O_RDONLY);
read(desk, &ch, 1);
countLetters(tab, ch);
printf("%c", ch);
}
}
void cleanUp(FILE *file) {
wait(0);
fclose(file);
close(desk);
}
int checkEntryData(int argc, char *argv) {
if (argc < 2) {
fprintf(stderr, "Nie poprawna ilosc argumentow\n");
return 0;
}
if (access(argv, F_OK)) {
fprintf(stderr, "Podany plik \'%s\' nie istnieje\n", argv);
return 0;
}
if (access(argv, R_OK)) {
fprintf(stderr, "Brak uprawnien do odczytu pliku \'%s\'\n", argv);
return 0;
}
return 1;
}
void replaceTabWithZero(int * tab) {
for (int i = 0; i < SIZE; i++)
tab[i] = 0;
}
void countLetters(int *tab, char ch) {
int chVal = ch;
if (chVal > 92)
chVal -= 32;
if (chVal > 64 && chVal < 91)
tab[chVal-65] += 1;
}
void saveResults(int *tab, char * title) {
FILE *plik = fopen(title, "w");
if (plik) {
for (int i = 0; i < SIZE; i++)
fprintf(plik, "%c - %d\n", (i+97), tab[i]);
} else {
perror("Error");
}
fclose(plik);
}
void showTab(int * tab) {
for (int i = 0; i < SIZE; i++)
printf("\n%d", tab[i]);
}
The real problem is that the client process can never finish, because it runs an infinite while(1) loop without any exit conditions.
You should rewrite it so that it exits after reading all available data:
void client() {
char ch;
// Open the fifo only once, instead of once per character
desk = open(FIFO, O_RDONLY);
// Loop until there is no more data to read
while(read(desk, &ch, 1) > 0) {
countLetters(tab, ch);
printf("%c", ch);
}
}
This is technically sufficient to make it work, but you should also look into a series of other issues:
You should have two wait(0) calls so that you wait for both processes, and you shouldn't try to kill anything.
The server process should only be opening the fifo once, not once per character.
You should be comparing fgetc output to EOF before forcing the value into a char. Since you do it after, running your program on a ISO-8859-1 terminal will cause it to confuse EOF and the letter ÿ
You are using strcat on argv[1], even though you don't know how much space that array has. You should use your own buffer of a known length.
You should check the return value of all your system calls to ensure they succeed. Checking with access and then assuming it'll be fine is not as good since calls can fail for other reasons.
Canonical Unix behavior is to exit with 0 for success, and >= 1 for error.
It's good practice to use a larger buffer (e.g. 65536 bytes instead of 1) when using read/write directly. stdio functions like fgetc already uses a larger buffer behind the scenes.
Using a named pipe obviously works, but since you spawn both processes it would be more natural to use an unnamed one.
I am trying to get zero copy semantics working in linux using
vmsplice()/splice() but I don't see any performance improvement. This
is on linux 3.10, tried on 3.0.0 and 2.6.32. The following code tries
to do file writes, I have tried network socket writes() also, couldn't
see any improvement.
Can somebody tell what am I doing wrong ?
Has anyone gotten improvement using vmsplice()/splice() in production ?
#include <assert.h>
#include <fcntl.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <vector>
const char *filename = "Test-File";
const int block_size = 4 * 1024;
const int file_size = 4 * 1024 * 1024;
using namespace std;
int pipes[2];
vector<char *> file_data;
static int NowUsecs() {
struct timeval tv;
const int err = gettimeofday(&tv, NULL);
assert(err >= 0);
return tv.tv_sec * 1000000LL + tv.tv_usec;
}
void CreateData() {
for (int xx = 0; xx < file_size / block_size; ++xx) {
// The data buffer to fill.
char *data = NULL;
assert(posix_memalign(reinterpret_cast<void **>(&data), 4096, block_size) == 0);
file_data.emplace_back(data);
}
}
int SpliceWrite(int fd, char *buf, int buf_len) {
int len = buf_len;
struct iovec iov;
iov.iov_base = buf;
iov.iov_len = len;
while (len) {
int ret = vmsplice(pipes[1], &iov, 1, SPLICE_F_GIFT);
assert(ret >= 0);
if (!ret)
break;
len -= ret;
if (len) {
auto ptr = static_cast<char *>(iov.iov_base);
ptr += ret;
iov.iov_base = ptr;
iov.iov_len -= ret;
}
}
len = buf_len;
while (len) {
int ret = splice(pipes[0], NULL, fd, NULL, len, SPLICE_F_MOVE);
assert(ret >= 0);
if (!ret)
break;
len -= ret;
}
return 1;
}
int WriteToFile(const char *filename, bool use_splice) {
// Open and write to the file.
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
int fd = open(filename, O_CREAT | O_RDWR, mode);
assert(fd >= 0);
const int start = NowUsecs();
for (int xx = 0; xx < file_size / block_size; ++xx) {
if (use_splice) {
SpliceWrite(fd, file_data[xx], block_size);
} else {
assert(write(fd, file_data[xx], block_size) == block_size);
}
}
const int time = NowUsecs() - start;
// Close file.
assert(close(fd) == 0);
return time;
}
void ValidateData() {
// Open and read from file.
const int fd = open(filename, O_RDWR);
assert(fd >= 0);
char *read_buf = (char *)malloc(block_size);
for (int xx = 0; xx < file_size / block_size; ++xx) {
assert(read(fd, read_buf, block_size) == block_size);
assert(memcmp(read_buf, file_data[xx], block_size) == 0);
}
// Close file.
assert(close(fd) == 0);
assert(unlink(filename) == 0);
}
int main(int argc, char **argv) {
auto res = pipe(pipes);
assert(res == 0);
CreateData();
const int without_splice = WriteToFile(filename, false /* use splice */);
ValidateData();
const int with_splice = WriteToFile(filename, true /* use splice */);
ValidateData();
cout << "TIME WITH SPLICE: " << with_splice << endl;
cout << "TIME WITHOUT SPLICE: " << without_splice << endl;
return 0;
}
I did a proof-of-concept some years ago where I got as 4x speedup using an optimized, specially tailored, vmsplice() code. This was measured against a generic socket/write() based solution. This blog post from natsys-lab echoes my findings. But I believe you need to have the exact right use case to get near this number.
So what are you doing wrong? Primarily I think you are measuring the wrong thing. When writing directly to a file you have 1 system call, which is write(). And you are not actually copying data (except to the kernel). When you have a buffer with data that you want to write to disk, it's not gonna get faster than that.
In you vmsplice/splice setup you are still copying you data into the kernel, but you have a total of 2 system calls vmsplice()+splice() to get it to disk. The speed being identical to write() is probably just a testament to Linux system call speed :-)
A more "fair" setup would be to write one program that read() from stdin and write() the same data to stdout. Write an identical program that simply splice() stdin into a file (or point stdout to a file when you run it). Although this setup might be too simple to really show anything.
Aside: an (undocumented?) feature of vmsplice() is that you can also use to to read data from a pipe. I used this in my old POC. It was basically just an IPC layer based on the idea of passing memory pages around using vmsplice().
Note: NowUsecs() probably overflows the int
I have a bit of a problem in using IPC (inter-process communication) program below.
Please let me explain:
I want to pass Linux commands such as "ls" or "wc file.txt"
from a parent to a child to execute using the message queue, and
then have the child returning the command outputs back to
the parent process using shared memory method.
But this is what I got: The parent process always got the output 1 step behind;
in the following fashion:
Step1) ls file.txt
(Nothing showed up.)
Step2) wc file.txt
(Output of earlier command "ls file.txt" showed up here instead.)
Step 3) cat file.txt
(Output of earlier command "wc file.txt" showed up instead.)
Any help is appreciated.
To compile: gcc -o program ./program.c
To run: -./program -v
Code:
#define BUFSZ 512
#define ERRBUFSZ 512
#define TIMEOUT_TIMEDIO 20
#define SHM_SIZE 5120
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <setjmp.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
static sigjmp_buf jmpbuf;
int timed_io(char* buf, int len, FILE* rfp, int sec);
static void sigalrm_handler(int signo);
void do_cmd(char *buf, int len, int linenum, char *errbuf);
int parse_cmd(char *buf, char **vbuf, char *errbuf);
int process_cmd_ipc(char *argv, int linenum, char *errbuf);
struct my_msgbuf {
long mtype;
char mtext[256];
};
static void sigalrm_handler(int signo)
{
siglongjmp(jmpbuf, 1);
}
int timed_io(char* buf, int len, FILE* rfp, int sec)
{
struct sigaction nsigaction[1];
struct sigaction osigaction[1];
int prev_alrm;
int st = 0;
if(sigsetjmp(jmpbuf, 1) == 0)
{
nsigaction->sa_handler = sigalrm_handler;
sigemptyset(&nsigaction->sa_mask);
nsigaction->sa_flags = SA_RESTART;
prev_alrm = alarm(0);
sigaction(SIGALRM, nsigaction, osigaction);
alarm(sec);
if (fgets(buf, len, rfp) == NULL)
st = -1; // EOF
buf[strlen(buf) - 1] = 0;
}
else { st = -2; } // Time-out
alarm(0); // Reset old alarm and handler
sigaction(SIGALRM, osigaction, 0);
return st;
}
int process_cmd_ipc(char *argv, int linenum, char* errbuf)
{
struct my_msgbuf buf;
int msqid, msqid_parent, st, shmid, str_len;
key_t key, key_shm;
char* shared_buf;
FILE *fd;
// create key for shared memory segment
if ((key_shm = ftok("shm_key.txt", 'R')) == -1) {
perror("ftok");
exit(1);
}
// Connect to shared memory segment
if ((shmid = shmget(key_shm, SHM_SIZE, 0644 | IPC_CREAT)) == -1)
{
perror("shmget");
exit(1);
}
// Attach to shared memory segment
shared_buf = shmat(shmid, (void *) 0, 0);
if (shared_buf == (char *) (-1)) {
perror("shmat");
exit(1);
}
// End of shared memory section` //
// Begin: message queue section
pid_t cpid=fork();
if (cpid<0) {
fprintf(stderr,"ERR: \"fork\" error! (Line=%d)\n", linenum);
exit (-1);
} else if (cpid==0) // child process
{ // Begin: message queue
if ((key = ftok("mysh.c", 'B')) == -1) {
perror("ftok");
exit(1);
}
if ((msqid = msgget(key, 0644)) == -1) {
perror("msgget from child");
exit(1);
}
memset(buf.mtext, 0, sizeof(buf.mtext)); // Clear buffer
if(msgrcv(msqid, (struct msgbuf*) &buf, sizeof(buf), 0,0) == -1)
{
perror("msgrcv");
exit(1);
}
// End: message queue
// begin: shared memory segment
memset(shared_buf, 0, SHM_SIZE); // zeroize shared_buf
fd = popen(buf.mtext, "r");
str_len = 0;
while(fgets(shared_buf + str_len, SHM_SIZE, fd) != NULL)
{ str_len = strlen(shared_buf); }
pclose(fd);
// end: shared memory segment
}
else { // parent
// Begin - message queue
if ((key = ftok("mysh.c", 'B')) == -1) {
perror("ftok");
exit(1);
}
if ((msqid_parent = msgget(key, 0644 | IPC_CREAT)) == -1) {
perror("msgget from parent");
exit(1);
}
buf.mtype = 1;
strncpy(buf.mtext, argv, strlen(argv));
if(msgsnd(msqid_parent, (struct my_msgbuf*) &buf, strlen(buf.mtext), 0) == -1)
perror("msgsnd");
// End - message queue
// Begin - shared memory
// usleep(10000);
printf("%s", shared_buf);
// End - shared memory
} // if-else fork
}
int parse_cmd(char *buf, char **vbuf, char *errbuf)
{
int i=0;
char *delim=" ,\t\n";
char *tok;
tok=strtok(buf,delim);
while (tok) {
vbuf[i]=(char *)malloc(BUFSZ*sizeof(char));
strcpy(vbuf[i],tok);
tok=strtok(NULL,delim);
i++;
}
vbuf[i]=0;
return i;
}
void do_cmd(char *buf, int len, int linenum, char *errbuf) {
int i=0; int numargs;
char *vbuf[128];
char* copy = (char *) malloc(strlen(buf) + 1);
int maxargs=sizeof(vbuf)/sizeof(char *);
strcpy(copy, buf);
numargs = parse_cmd(copy,vbuf,errbuf);
process_cmd_ipc(buf,linenum, errbuf);
for (i=0;i<numargs; i++) { free(vbuf[i]); }
free(copy);
copy = NULL;
return;
}
int main(int argc, char **argv)
{
int i; int st; int linenum=0;
char *buf=(char *)malloc(BUFSZ*sizeof(char));
char *errbuf=(char *)malloc(ERRBUFSZ*sizeof(char));
char *mysh = "";
FILE *rfp=stdin;
if (isatty(fileno(rfp))) {
mysh = "mysh (Ctrl-C to exit)>";
fprintf(stderr,"%s",mysh);
}
while(1)
{
st = timed_io(buf, BUFSZ, stdin, TIMEOUT_TIMEDIO);
if (st != 0)
{
fprintf(stderr, "ERR: No input %s (Status=%d)\n", errbuf, st);
return -1;
}
else
{
linenum++;
if (*buf)
{ do_cmd(buf, BUFSZ, linenum,errbuf); }
if (mysh)
fprintf(stderr,"%s",mysh);
}
}
}