1 //
2 // aegis - project change supervisor
3 // Copyright (C) 1994-1999, 2001, 2003-2006, 2008, 2012 Peter Miller
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or (at
8 // your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 //
18 
19 #include <common/ac/assert.h>
20 #include <common/ac/errno.h>
21 #include <common/ac/fcntl.h>
22 #include <common/ac/unistd.h>
23 #include <common/ac/sys/types.h>
24 #include <sys/socket.h>
25 
26 #include <common/mem.h>
27 #include <common/trace.h>
28 #include <libaegis/glue.h>
29 #include <libaegis/input/curl.h>
30 #include <libaegis/input/file.h>
31 #include <libaegis/input/stdin.h>
32 #include <libaegis/os.h>
33 #include <libaegis/sub.h>
34 #include <libaegis/url.h>
35 
36 
~input_file()37 input_file::~input_file()
38 {
39     trace(("input_file::~input_file()\n{\n"));
40     if (fd >= 0)
41     {
42         if (glue_close(fd))
43         {
44             int errno_old = errno;
45             sub_context_ty *scp = sub_context_new();
46             sub_errno_setx(scp, errno_old);
47             sub_var_set_string(scp, "File_Name", path);
48             fatal_intl(scp, i18n("close $filename: $errno"));
49             // NOTREACHED
50         }
51 
52         // The file only exists if fd>=0
53         if (unlink_on_close_flag)
54             os_unlink_errok(path);
55 
56         fd = -1;
57     }
58     trace(("}\n"));
59 }
60 
61 
62 static int
open_with_stale_nfs_retry(const char * path,int mode)63 open_with_stale_nfs_retry(const char *path, int mode)
64 {
65     trace(("open_with_stale_nfs_retry(path = \"%s\", mode = %d)\n{\n", path,
66         mode));
67     //
68     // Try to open the file.
69     //
70     errno = 0;
71     int perms = 0666;
72     int fd = glue_open(path, mode, perms);
73 
74     //
75     // Keep trying for one minute if we get a Stale NFS file handle
76     // error.  Some systems suffer from this in a Very Bad Way.
77     //
78 #ifdef ESTALE
79     const int nsecs = 5;
80     for (int ntries = 0; ntries < 60; ntries += nsecs)
81     {
82         if (fd >= 0)
83             break;
84         if (errno != ESTALE)
85             break;
86         sleep(nsecs);
87         errno = 0;
88         fd = glue_open(path, mode, perms);
89     }
90 #endif
91 
92     //
93     // Return the result, both success and failure.
94     // Errors are handled elsewhere.
95     //
96     trace(("return %d\n", fd));
97     trace(("}\n"));
98     return fd;
99 }
100 
101 
input_file(const nstring & arg1,bool arg2,bool empty_if_absent)102 input_file::input_file(const nstring &arg1, bool arg2, bool empty_if_absent) :
103     path(arg1),
104     fd(-1),
105     unlink_on_close_flag(arg2),
106     pos(0)
107 {
108     trace(("input_file::input_file(\"%s\")\n{\n", arg1.c_str()));
109     os_become_must_be_active();
110     int mode = O_RDONLY;
111 #if defined(__CYGWIN__) || defined(__CYGWIN32__)
112     // I'm not sure whether MacOsX uses \r or \n in its native text
113     // files, so I'm reluctant to always use the O_BINARY mode bit.
114     mode |= O_BINARY;
115 #endif
116     fd = open_with_stale_nfs_retry(path.c_str(), mode);
117     if (fd < 0)
118     {
119         int errno_old = errno;
120         if (!empty_if_absent || errno_old != ENOENT)
121         {
122             sub_context_ty sc(__FILE__, __LINE__);
123             sc.errno_setx(errno_old);
124             sc.var_set_string("File_Name", path);
125             sc.fatal_intl(i18n("open $filename: $errno"));
126             // NOTREACHED
127         }
128     }
129     trace(("}\n"));
130 }
131 
132 
133 ssize_t
read_inner(void * data,size_t len)134 input_file::read_inner(void *data, size_t len)
135 {
136     trace(("input_file::read_inner()\n"));
137     assert(reference_count_valid());
138     os_become_must_be_active();
139     if (len == 0)
140         return 0;
141     if (fd < 0)
142         return 0;
143     ssize_t result = glue_read(fd, data, len);
144     if (result < 0)
145     {
146         int errno_old = errno;
147         sub_context_ty sc;
148         sc.errno_setx(errno_old);
149         sc.var_set_string("File_Name", path);
150         sc.fatal_intl(i18n("read $filename: $errno"));
151         // NOTREACHED
152     }
153     pos += result;
154     return result;
155 }
156 
157 
158 off_t
ftell_inner()159 input_file::ftell_inner()
160 {
161     assert(reference_count_valid());
162     return pos;
163 }
164 
165 
166 nstring
name()167 input_file::name()
168 {
169     assert(reference_count_valid());
170     return path;
171 }
172 
173 
174 off_t
length()175 input_file::length()
176 {
177     assert(reference_count_valid());
178     if (fd < 0)
179         return 0;
180     return os_file_size(path.get_ref());
181 }
182 
183 
184 void
keepalive()185 input_file::keepalive()
186 {
187     assert(reference_count_valid());
188     if (fd >= 0)
189     {
190         int on = 1;
191         // ignore any error
192         setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof(on));
193     }
194 }
195 
196 
197 void
unlink_on_close()198 input_file::unlink_on_close()
199 {
200     assert(reference_count_valid());
201     unlink_on_close_flag = true;
202 }
203 
204 
205 input
input_file_open(const nstring & fn,bool unlink_on_close,bool empty_if_absent)206 input_file_open(const nstring &fn, bool unlink_on_close, bool empty_if_absent)
207 {
208     os_become_must_be_active();
209     if (fn.empty() || fn == "-")
210         return new input_stdin();
211     if (input_curl::looks_likely(fn))
212         return new input_curl(fn);
213     return new input_file(fn, unlink_on_close, empty_if_absent);
214 }
215 
216 
217 input
input_file_open(string_ty * fn,bool unlink_on_close)218 input_file_open(string_ty *fn, bool unlink_on_close)
219 {
220     return input_file_open(nstring(fn), unlink_on_close);
221 }
222 
223 
224 // vim: set ts=8 sw=4 et :
225