mirror of
				https://github.com/ossrs/srs.git
				synced 2025-03-09 15:49:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			197 lines
		
	
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			197 lines
		
	
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * File I/O extension to the State Threads Library.
 | |
|  */
 | |
| 
 | |
| /* 
 | |
|  * The contents of this file are subject to the Mozilla Public
 | |
|  * License Version 1.1 (the "License"); you may not use this file
 | |
|  * except in compliance with the License. You may obtain a copy of
 | |
|  * the License at http://www.mozilla.org/MPL/
 | |
|  * 
 | |
|  * Software distributed under the License is distributed on an "AS
 | |
|  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 | |
|  * implied. See the License for the specific language governing
 | |
|  * rights and limitations under the License.
 | |
|  * 
 | |
|  * The Original Code is the file I/O extension to the State Threads Library.
 | |
|  * 
 | |
|  * The Initial Developer of the Original Code is Jeff
 | |
|  * <jlb-st@houseofdistraction.com>.  Portions created by the Initial
 | |
|  * Developer are Copyright (C) 2002 the Initial Developer.  All Rights
 | |
|  * Reserved.
 | |
|  * 
 | |
|  * Contributor(s): (none)
 | |
|  * 
 | |
|  * Alternatively, the contents of this file may be used under the
 | |
|  * terms of the GNU General Public License Version 2 or later (the
 | |
|  * "GPL"), in which case the provisions of the GPL are applicable 
 | |
|  * instead of those above.  If you wish to allow use of your 
 | |
|  * version of this file only under the terms of the GPL and not to
 | |
|  * allow others to use your version of this file under the MPL,
 | |
|  * indicate your decision by deleting the provisions above and
 | |
|  * replace them with the notice and other provisions required by
 | |
|  * the GPL.  If you do not delete the provisions above, a recipient
 | |
|  * may use your version of this file under either the MPL or the
 | |
|  * GPL.
 | |
|  */
 | |
| 
 | |
| #include <stdlib.h>
 | |
| 
 | |
| #include "stx_fileio.h"
 | |
| 
 | |
| #define STX_FILEIO_SIGNUM SIGUSR2
 | |
| 
 | |
| typedef struct {
 | |
|     st_netfd_t data_fd;
 | |
|     st_netfd_t control_fd;
 | |
|     pid_t pid;
 | |
| } fileio_data_t;
 | |
| 
 | |
| #define FILEREADER_MAX_READ 1024
 | |
| 
 | |
| typedef struct {
 | |
|     off_t offset;
 | |
|     ssize_t nbytes;
 | |
| } file_reader_cb_t;
 | |
| 
 | |
| /**
 | |
|  * Fork a process to read a file and return its pid.  Receives
 | |
|  * offset/length commands from control stream and sends corresponding data
 | |
|  * to out stream.  A zero length on the control stream signals an end.
 | |
|  *
 | |
|  * @param fd stream from which to read
 | |
|  * @param control_out receives the file descriptor to which control commands can be sent
 | |
|  * @param fd_out receives the file descriptor from which the output of the command can be read.
 | |
|  * @return PID of the process created to execute the command
 | |
|  */
 | |
| pid_t
 | |
| file_reader(int fd, int *fd_control, int *fd_out)
 | |
| {
 | |
|     pid_t pid;
 | |
|     int control_pipe[2], out_pipe[2];
 | |
| 
 | |
|     if (pipe(control_pipe) < 0 || pipe(out_pipe) < 0)
 | |
|         return (pid_t)-1;
 | |
| 
 | |
|     pid = fork();
 | |
|     if (pid == (pid_t) -1)
 | |
|     {
 | |
|         close(control_pipe[0]);
 | |
|         close(control_pipe[1]);
 | |
|         close(out_pipe[0]);
 | |
|         close(out_pipe[1]);
 | |
|         return pid;
 | |
|     }
 | |
|     else if (pid == (pid_t) 0)
 | |
|     {
 | |
|         // child
 | |
|         off_t pos = 0;
 | |
|         file_reader_cb_t cb;
 | |
|         char buf[FILEREADER_MAX_READ];
 | |
|         if (fd == -1)
 | |
|             _exit(EXIT_FAILURE);
 | |
| 
 | |
|         while (sizeof(cb) == read(control_pipe[0], &cb, sizeof(cb))) {
 | |
|             ssize_t nb;
 | |
|             if (0 >= cb.nbytes)
 | |
|                 goto clean_exit;
 | |
|             if (pos != cb.offset) {
 | |
|                 pos = lseek(fd, cb.offset, SEEK_SET);
 | |
|                 if (pos == (off_t)-1)
 | |
|                     break;
 | |
|             }
 | |
|             nb = read(fd, buf, cb.nbytes);
 | |
|             if (nb == (ssize_t)-1)
 | |
|                 break;
 | |
|             pos += nb;
 | |
|             write(out_pipe[1], (char *)&nb, sizeof(nb));
 | |
|             write(out_pipe[1], buf, nb);
 | |
|         }
 | |
|         perror("ERROR: file_reader: ");
 | |
|     clean_exit:
 | |
|         close(control_pipe[0]);
 | |
|         close(control_pipe[1]);
 | |
|         close(out_pipe[0]);
 | |
|         close(out_pipe[1]);
 | |
|         _exit(EXIT_SUCCESS);
 | |
|     }
 | |
| 
 | |
|     // parent
 | |
|     close(out_pipe[1]);
 | |
|     close(control_pipe[0]);
 | |
|     *fd_out = out_pipe[0];
 | |
|     *fd_control = control_pipe[1];
 | |
|     return pid;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * fileio_data_t destructor callback
 | |
|  */
 | |
| static void
 | |
| fileio_data_destructor(void *dat_in)
 | |
| {
 | |
|     if (dat_in) {
 | |
|         fileio_data_t *dat = (fileio_data_t *)dat_in;
 | |
|         file_reader_cb_t cb;
 | |
|         cb.offset = 0;
 | |
|         cb.nbytes = 0;
 | |
|         st_write(dat->control_fd, (char *)&cb, sizeof(cb),
 | |
| 	 ST_UTIME_NO_TIMEOUT);
 | |
|         waitpid(dat->pid, NULL, 0);
 | |
|         st_netfd_close(dat->control_fd);
 | |
|         st_netfd_close(dat->data_fd);
 | |
|         free(dat_in);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Retrieve fileio_data_t struct from an st descriptor.  Create and store
 | |
|  * a new one if needed.
 | |
|  */
 | |
| static fileio_data_t *get_fileio_data(st_netfd_t fd)
 | |
| {
 | |
|     fileio_data_t *dat = (fileio_data_t *)st_netfd_getspecific(fd);
 | |
|     if (!dat) {
 | |
|         int fd_control, fd_out;
 | |
|         pid_t pid = file_reader(st_netfd_fileno(fd), &fd_control, &fd_out);
 | |
|         if (pid != (pid_t)-1) {
 | |
|             dat = (fileio_data_t *)calloc(1, sizeof(fileio_data_t));
 | |
|             dat->control_fd = st_netfd_open(fd_control);
 | |
|             dat->data_fd = st_netfd_open(fd_out);
 | |
|             dat->pid = pid;
 | |
|             st_netfd_setspecific(fd, dat, fileio_data_destructor);
 | |
|         }
 | |
|     }
 | |
|     return dat;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Read data from the specified section of a file.  Uses a forked
 | |
|  * file_reader process to do the actual reading so as to avoid causing all
 | |
|  * State Threads to block.
 | |
|  *
 | |
|  * @param fd must refer to a seekable file.
 | |
|  * @param offset absolute offset within the file
 | |
|  * @param buf output buffer
 | |
|  * @param nbytes size of the output buffer
 | |
|  * @param timeout
 | |
|  */
 | |
| ssize_t
 | |
| stx_file_read(st_netfd_t fd, off_t offset, void *buf, size_t nbytes, st_utime_t timeout)
 | |
| {
 | |
|     fileio_data_t *dat = get_fileio_data(fd);
 | |
|     if (dat) {
 | |
|         file_reader_cb_t cb;
 | |
|         ssize_t ret = (ssize_t)-1;
 | |
|         cb.offset = offset;
 | |
|         cb.nbytes = nbytes;
 | |
|         st_write(dat->control_fd, (char *)&cb, sizeof(cb), timeout);
 | |
|         if (sizeof(ret) == st_read(dat->data_fd, (char *)&ret, sizeof(ret), timeout) && 0 < ret && ret <= nbytes) {
 | |
|             return st_read(dat->data_fd, buf, ret, timeout);
 | |
|         } else {
 | |
|             return ret;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return (ssize_t)-1;
 | |
| }
 |