简体   繁体   English

读取系统调用:当输入的字节数超过计数参数时,多余的字节溢出到 shell 并作为下一条命令执行

[英]read system call: When input has more bytes than count argument, excess bytes overflow to shell and get executed as next command

I am writing a code to Implement tee command using I/O system calls .我正在编写代码来使用 I/O 系统调用来实现tee命令 This is an exercise in the book The Linux Programming Interface by Michael Kerrisk.这是 Michael Kerrisk 所著的 The Linux Programming Interface一书中的一个练习。

My system is Ubuntu 16.04 .我的系统是Ubuntu 16.04

I am inexperienced with linux programming.我对 linux 编程缺乏经验。

  #include <sys/stat.h>                                                                                                                                                                                             
  #include <fcntl.h>
  #include <unistd.h>
  #include "tlpi_hdr.h"
  
  #define MAX_READ 20
  
  int
  main(int argc, char *argv[])
  {
    int fileFd;
    ssize_t numRead;
    char buffer[MAX_READ + 10];
  
    // command example: tee_practice tfile
    if (argc != 2 || strcmp(argv[1], "--help") == 0)
      usageErr("Usage error\nDid you supply the filename?\n");
  
    fileFd = open(argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
  
    if (fileFd == -1)
      errExit("open");
  
    if ((numRead = read(STDIN_FILENO, buffer, MAX_READ)) == -1) // HERE bytes overflow to shell and get executed as next command
      errExit("read");
  
    buffer[numRead] = '\0';
  
    if (write(STDOUT_FILENO, buffer, MAX_READ) == -1)
      errExit("write");
  
    if (write(fileFd, buffer, MAX_READ) == -1)
      errExit("write");
  
    exit(EXIT_SUCCESS);
  }

On input输入时

Hi I am writing a few lines嗨,我正在写几行

the program writes first 20 bytes ( Hi I am writing a fe ) to file and stdout.该程序将前 20 个字节(嗨,我正在写一个 fe )写入文件和标准输出。 The remaining bytes ( w lines ) are executed as next shell command, which I want to prevent from happening.剩余的字节( w lines )作为下一个 shell 命令执行,我想防止这种情况发生。

Where am I going wrong?我哪里错了?


EDIT编辑

Not exactly minimal reproducible example, but it works and includes errExit() and usageErro().不完全是最小的可重现示例,但它可以工作并且包括 errExit() 和 usageErro()。

#include <sys/stat.h>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
#include <fcntl.h>
#include <unistd.h>
#include <stdarg.h>

#include <sys/types.h>  /* Type definitions used by many programs */
#include <stdio.h>      /* Standard I/O functions */
#include <stdlib.h>     /* Prototypes of commonly used library functions,
                           plus EXIT_SUCCESS and EXIT_FAILURE constants */
#include <unistd.h>     /* Prototypes for many system calls */
#include <errno.h>      /* Declares errno and defines error constants */
#include <string.h>     /* Commonly used string-handling functions */

#define MAX_READ 20

#ifndef ERROR_FUNCTIONS_H
#define ERROR_FUNCTIONS_H


#ifdef __GNUC__

    /* This macro stops 'gcc -Wall' complaining that "control reaches
       end of non-void function" if we use the following functions to
       terminate main() or some other non-void function. */

#define NORETURN __attribute__ ((__noreturn__))
#else
#define NORETURN
#endif

#endif

void errMsg(const char *format, ...);

void errExit(const char *format, ...) NORETURN ;

void usageErr(const char *format, ...) NORETURN ;

typedef enum { FALSE, TRUE } Boolean;

static char *ename[] = {
    /*   0 */ "", 
    /*   1 */ "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO", "ENXIO", 
    /*   7 */ "E2BIG", "ENOEXEC", "EBADF", "ECHILD", 
    /*  11 */ "EAGAIN/EWOULDBLOCK", "ENOMEM", "EACCES", "EFAULT", 
    /*  15 */ "ENOTBLK", "EBUSY", "EEXIST", "EXDEV", "ENODEV", 
    /*  20 */ "ENOTDIR", "EISDIR", "EINVAL", "ENFILE", "EMFILE", 
    /*  25 */ "ENOTTY", "ETXTBSY", "EFBIG", "ENOSPC", "ESPIPE", 
    /*  30 */ "EROFS", "EMLINK", "EPIPE", "EDOM", "ERANGE", 
    /*  35 */ "EDEADLK/EDEADLOCK", "ENAMETOOLONG", "ENOLCK", "ENOSYS", 
    /*  39 */ "ENOTEMPTY", "ELOOP", "", "ENOMSG", "EIDRM", "ECHRNG", 
    /*  45 */ "EL2NSYNC", "EL3HLT", "EL3RST", "ELNRNG", "EUNATCH", 
    /*  50 */ "ENOCSI", "EL2HLT", "EBADE", "EBADR", "EXFULL", "ENOANO", 
    /*  56 */ "EBADRQC", "EBADSLT", "", "EBFONT", "ENOSTR", "ENODATA", 
    /*  62 */ "ETIME", "ENOSR", "ENONET", "ENOPKG", "EREMOTE", 
    /*  67 */ "ENOLINK", "EADV", "ESRMNT", "ECOMM", "EPROTO", 
    /*  72 */ "EMULTIHOP", "EDOTDOT", "EBADMSG", "EOVERFLOW", 
    /*  76 */ "ENOTUNIQ", "EBADFD", "EREMCHG", "ELIBACC", "ELIBBAD", 
    /*  81 */ "ELIBSCN", "ELIBMAX", "ELIBEXEC", "EILSEQ", "ERESTART", 
    /*  86 */ "ESTRPIPE", "EUSERS", "ENOTSOCK", "EDESTADDRREQ", 
    /*  90 */ "EMSGSIZE", "EPROTOTYPE", "ENOPROTOOPT", 
    /*  93 */ "EPROTONOSUPPORT", "ESOCKTNOSUPPORT", 
    /*  95 */ "EOPNOTSUPP/ENOTSUP", "EPFNOSUPPORT", "EAFNOSUPPORT", 
    /*  98 */ "EADDRINUSE", "EADDRNOTAVAIL", "ENETDOWN", "ENETUNREACH", 
    /* 102 */ "ENETRESET", "ECONNABORTED", "ECONNRESET", "ENOBUFS", 
    /* 106 */ "EISCONN", "ENOTCONN", "ESHUTDOWN", "ETOOMANYREFS", 
    /* 110 */ "ETIMEDOUT", "ECONNREFUSED", "EHOSTDOWN", "EHOSTUNREACH", 
    /* 114 */ "EALREADY", "EINPROGRESS", "ESTALE", "EUCLEAN", 
    /* 118 */ "ENOTNAM", "ENAVAIL", "EISNAM", "EREMOTEIO", "EDQUOT", 
    /* 123 */ "ENOMEDIUM", "EMEDIUMTYPE", "ECANCELED", "ENOKEY", 
    /* 127 */ "EKEYEXPIRED", "EKEYREVOKED", "EKEYREJECTED", 
    /* 130 */ "EOWNERDEAD", "ENOTRECOVERABLE", "ERFKILL", "EHWPOISON"
};

#define MAX_ENAME 133

#ifdef __GNUC__
__attribute__ ((__noreturn__))
#endif

static void
terminate(Boolean useExit3)
{
    char *s;

    /* Dump core if EF_DUMPCORE environment variable is defined and
       is a nonempty string; otherwise call exit(3) or _exit(2),
       depending on the value of 'useExit3'. */

    s = getenv("EF_DUMPCORE");

    if (s != NULL && *s != '\0')
        abort();
    else if (useExit3)
        exit(EXIT_FAILURE);
    else
        _exit(EXIT_FAILURE);
}

static void
outputError(Boolean useErr, int err, Boolean flushStdout,
        const char *format, va_list ap)
{
#define BUF_SIZE 500
    char buf[BUF_SIZE], userMsg[BUF_SIZE], errText[BUF_SIZE];

    vsnprintf(userMsg, BUF_SIZE, format, ap);

    if (useErr)
        snprintf(errText, BUF_SIZE, " [%s %s]",
                (err > 0 && err <= MAX_ENAME) ?
                ename[err] : "?UNKNOWN?", strerror(err));
    else
        snprintf(errText, BUF_SIZE, ":");

    snprintf(buf, BUF_SIZE, "ERROR%s %s\n", errText, userMsg);

    if (flushStdout)
        fflush(stdout);       /* Flush any pending stdout */
    fputs(buf, stderr);
    fflush(stderr);           /* In case stderr is not line-buffered */
}

void
usageErr(const char *format, ...)
{
    va_list argList;

    fflush(stdout);           /* Flush any pending stdout */

    fprintf(stderr, "Usage: ");
    va_start(argList, format);
    vfprintf(stderr, format, argList);
    va_end(argList);

    fflush(stderr);           /* In case stderr is not line-buffered */
    exit(EXIT_FAILURE);
}

void
errExit(const char *format, ...)
{
    va_list argList;

    va_start(argList, format);
    outputError(TRUE, errno, TRUE, format, argList);
    va_end(argList);

    terminate(TRUE);
}

int
main(int argc, char *argv[])
{
  int fileFd;
  ssize_t numRead;
  char buffer[MAX_READ + 10];

  if (argc != 2 || strcmp(argv[1], "--help") == 0)
    usageErr("Usage error\nDid you supply the filename?\n");

  fileFd = open(argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);

  if (fileFd == -1)
    errExit("open");

  if ((numRead = read(STDIN_FILENO, buffer, MAX_READ)) == -1)
    errExit("read");

  buffer[numRead] = '\0';

  if (write(STDOUT_FILENO, buffer, MAX_READ) == -1)
    errExit("write");

  if (write(fileFd, buffer, MAX_READ) == -1)
    errExit("write");

  exit(EXIT_SUCCESS);
}

Your program only reads 20 bytes.您的程序只读取 20 个字节。 Whatever there is to be read after those 20 bytes, from whatever the program's standard input is connected to, stays there.在这 20 个字节之后要读取的任何内容,无论程序的标准输入连接到什么,都保留在那里。 Be that input from the terminal, a pipe buffer, or a file.无论是来自终端的输入、pipe 缓冲区还是文件。

If you used the stdio input functions ( fgets() , fread() etc.) instead, they would ask the OS for a larger block of data (usually 4096 B with glibc on Linux), so the issue wouldn't come up with such a short input.如果您使用 stdio 输入函数( fgets()fread()等),它们会要求操作系统提供更大的数据块(在 Linux 上通常为 4096 B 和 glibc),因此不会出现问题这么短的输入。

To get everything there is, you need to loop, reading everything until EOF and, since you're implementing tee , also copy all of it to standard output and the output file.要获得所有内容,您需要循环读取所有内容,直到 EOF 为止,并且由于您正在实施tee ,因此还将所有内容复制到标准 output 和 output 文件中。

ie something in this direction:即在这个方向上的东西:

#include<unistd.h>
#include<stdio.h>

int main(void)
{
    /* setup ... */
    char buf[1024];
    while(1) {
        int n = read(fd, buf, 1024);
        if (n == 0)
            break; /* EOF */
        if (n == -1) {
            perror("read");
            return 1;
        }
        write(STDOUT_FILENO, buf, n);
        write(outfd, buf, n);
    }
    return 0;
}

But check for errors on the write() calls, too.但也要检查write()调用中的错误。 Also, technically, write() may return without writing everything you asked, ie write(outfd, buf, n) may write less than the n bytes you asked.此外,从技术上讲, write()可能会返回而不写入您要求的所有内容,即write(outfd, buf, n)可能会写入少于您要求的n个字节。 But the cases where that happens are somewhat rare.但发生这种情况的情况很少见。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM