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