1 |
/* |
2 |
openisis - an open implementation of the CDS/ISIS database |
3 |
Version 0.8.x (patchlevel see file Version) |
4 |
Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org |
5 |
|
6 |
This library is free software; you can redistribute it and/or |
7 |
modify it under the terms of the GNU Lesser General Public |
8 |
License as published by the Free Software Foundation; either |
9 |
version 2.1 of the License, or (at your option) any later version. |
10 |
|
11 |
This library is distributed in the hope that it will be useful, |
12 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 |
Lesser General Public License for more details. |
15 |
|
16 |
You should have received a copy of the GNU Lesser General Public |
17 |
License along with this library; if not, write to the Free Software |
18 |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 |
|
20 |
see README for more information |
21 |
EOH */ |
22 |
|
23 |
/* |
24 |
$Id: lio.c,v 1.43 2003/06/11 14:53:08 kripke Exp $ |
25 |
I/O support for the openisis library. |
26 |
*/ |
27 |
#include <stdlib.h> |
28 |
#include <stdio.h> /* vsnprintf */ |
29 |
#include <stdarg.h> |
30 |
#include <string.h> |
31 |
#include <errno.h> |
32 |
#include <limits.h> /* PATH_MAX */ |
33 |
#include <sys/types.h> |
34 |
|
35 |
#ifdef WIN32 |
36 |
#define WIN32_LEAN_AND_MEAN |
37 |
#define NONAMELESSUNION |
38 |
# include <windows.h> |
39 |
# include <winsock.h> |
40 |
# define SHUT_RD SD_RECEIVE |
41 |
# define SHUT_WR SD_SEND |
42 |
# define SHUT_RDWR SD_BOTH |
43 |
# define snprintf _snprintf |
44 |
# define vsnprintf _vsnprintf |
45 |
# include <sys/timeb.h> |
46 |
#else /* have operating system */ |
47 |
# define __USE_UNIX98 |
48 |
/* |
49 |
# define _POSIX_MAPPED_FILES |
50 |
# define _POSIX_SYNCHRONIZED_IO |
51 |
*/ |
52 |
# include <fcntl.h> /* or try unixio */ |
53 |
# ifndef O_SYNC |
54 |
# if defined( __FreeBSD__ ) |
55 |
# define O_SYNC O_FSYNC |
56 |
# else |
57 |
# define O_SYNC 0 |
58 |
# endif |
59 |
# endif |
60 |
# include <sys/stat.h> |
61 |
# include <sys/file.h> /* flock */ |
62 |
# include <sys/mman.h> |
63 |
# include <sys/socket.h> |
64 |
# include <unistd.h> |
65 |
#endif /* WIN32 */ |
66 |
#include <sys/time.h> /* gettimeofday */ |
67 |
#include <time.h> /* localtime */ |
68 |
|
69 |
#include "luti.h" /* logging */ |
70 |
#include "lses.h" |
71 |
#include "lstr.h" /* log_str */ |
72 |
#include "lio.h" |
73 |
|
74 |
|
75 |
|
76 |
/* ************************************************************ |
77 |
private data |
78 |
*/ |
79 |
static int init; |
80 |
|
81 |
#ifdef WIN32 |
82 |
/* it's not thread-safe, anyway */ |
83 |
static HANDLE hand[128]; |
84 |
#endif |
85 |
|
86 |
/* ************************************************************ |
87 |
private functions |
88 |
*/ |
89 |
#ifndef WIN32 |
90 |
static int lerrno ( int errno_ ) |
91 |
{ |
92 |
switch ( errno_ ) { |
93 |
case EINTR: |
94 |
case EAGAIN: |
95 |
return ERR_OK; |
96 |
case EFAULT: /* structurally bad address */ |
97 |
case ENAMETOOLONG: |
98 |
case ELOOP: |
99 |
return ERR_FAULT; |
100 |
case EBADF: /* file "does not exist" */ |
101 |
case ENOENT: |
102 |
case ENOTDIR: |
103 |
return ERR_BADF; |
104 |
case EIO: /* file "is not accessible" */ |
105 |
case EEXIST: |
106 |
case EISDIR: |
107 |
case EACCES: |
108 |
case ENXIO: |
109 |
case ENODEV: |
110 |
case EROFS: |
111 |
case ETXTBSY: |
112 |
case ENOSPC: |
113 |
case EPIPE: |
114 |
case ESPIPE: |
115 |
return ERR_IO; |
116 |
case ENOMEM: |
117 |
case EMFILE: |
118 |
case ENFILE: |
119 |
return ERR_NOMEM; |
120 |
case EBUSY: |
121 |
return ERR_BUSY; |
122 |
} |
123 |
return ERR_INVAL; |
124 |
} /* lerrno */ |
125 |
|
126 |
/** |
127 |
check unixio errno. |
128 |
if harmless, return != 0 (is ok). |
129 |
else return 0, possibly closing the file |
130 |
*/ |
131 |
int unixio_ok ( int *file, int io ) |
132 |
{ |
133 |
switch ( errno ) { |
134 |
case EINTR: |
135 |
case EAGAIN: |
136 |
return !0; |
137 |
case EFAULT: |
138 |
case ESPIPE: |
139 |
break; |
140 |
default: |
141 |
log_msg( LOG_IOERR, "closing file %x for %x on %d", *file, io, errno ); |
142 |
lio_close( file, io ); |
143 |
} |
144 |
return 0; |
145 |
} /* unixio_err */ |
146 |
#endif |
147 |
|
148 |
|
149 |
/** |
150 |
return wether printing should be skipped |
151 |
*/ |
152 |
static int seterrlev ( int *err, int *level, int code ) |
153 |
{ |
154 |
*level = LOG_MASK & code; |
155 |
*err = ERR_MASK & code; |
156 |
if ( ! code ) |
157 |
return 0; |
158 |
if ( ! *err ) |
159 |
switch ( *level ) { |
160 |
case LOG_FATAL: *err = ERR_IDIOT; break; |
161 |
case LOG_ERROR: *err = ERR_INVAL; break; |
162 |
case LOG_IOERR: |
163 |
#ifndef WIN32 |
164 |
if ( EAGAIN == errno || EINTR == errno ) /* no error */ |
165 |
return !0; |
166 |
case LOG_SYSERR: |
167 |
*err = lerrno( errno ); |
168 |
#else |
169 |
case LOG_SYSERR: |
170 |
*err = ERR_INVAL; |
171 |
#endif |
172 |
break; |
173 |
default: |
174 |
*err = 0; |
175 |
} |
176 |
else if ( ! *level ) { |
177 |
if ( ERR_TRASH <= *err ) |
178 |
*level = LOG_FATAL; |
179 |
else if ( ERR_IO <= *err ) |
180 |
*level = LOG_SYSERR; |
181 |
else if ( ERR_FAULT <= *err ) |
182 |
*level = LOG_ERROR; |
183 |
else |
184 |
*level = LOG_VERBOSE; |
185 |
} |
186 |
return *level > (int)log_lev; |
187 |
} /* seterrlev */ |
188 |
|
189 |
|
190 |
/* ************************************************************ |
191 |
package data |
192 |
*/ |
193 |
|
194 |
CLockFunc *lio_lock; |
195 |
LogLevel log_lev = LOG_ERROR; |
196 |
int log_flush = 1; |
197 |
|
198 |
#ifndef WIN32 |
199 |
# define LIO_STATICFD( nm, flg ) int nm = flg |
200 |
#else |
201 |
# define LIO_STATICFD( nm, flg ) int nm |
202 |
#endif |
203 |
LIO_STATICFD( lio_in , LIO_IN | 0 ); |
204 |
LIO_STATICFD( lio_out, LIO_OUT | 1 ); |
205 |
LIO_STATICFD( lio_err, LIO_OUT | 2 ); |
206 |
|
207 |
/* ************************************************************ |
208 |
package functions |
209 |
*/ |
210 |
static void lio_init () |
211 |
{ |
212 |
#ifndef WIN32 |
213 |
/* static */ |
214 |
#else |
215 |
lio_in = lio_open( "CONIN$", LIO_RD ); |
216 |
lio_out = lio_open( "CONOUT$", LIO_WR ); |
217 |
lio_err = lio_open( "oisiserr.txt", LIO_WR|LIO_CREAT ); |
218 |
#endif |
219 |
} /* lio_init */ |
220 |
|
221 |
|
222 |
/* |
223 |
has "official" name, since visible to linker |
224 |
use ld -init openIsisInit for shared object |
225 |
*/ |
226 |
void openIsisInit () |
227 |
{ |
228 |
static const char inimsg[] = "openIsisInit\n"; |
229 |
const char *ll = getenv("OPENISIS_LOGLEVEL"); |
230 |
|
231 |
if ( init ) |
232 |
return; |
233 |
init = 42; |
234 |
lio_init(); |
235 |
if ( ll ) { |
236 |
cLog( ll[0], 0 ); |
237 |
#ifndef NDEBUG |
238 |
if ( LOG_DEBUG <= log_lev ) |
239 |
lio_write( &lio_err, inimsg, sizeof(inimsg)-1 ); |
240 |
#endif |
241 |
} |
242 |
if ( getenv("OPENISIS_LOGBUFFERED") ) |
243 |
log_flush = 0; |
244 |
lses_init(); |
245 |
} /* openIsisInit */ |
246 |
|
247 |
|
248 |
/* |
249 |
use ld -fini openIsisFini for shared object |
250 |
*/ |
251 |
void openIsisFini () |
252 |
{ |
253 |
static const char finimsg[] = "openIsisFini\n"; |
254 |
|
255 |
lses_fini(); |
256 |
#ifndef NDEBUG |
257 |
if ( LOG_DEBUG <= log_lev ) |
258 |
lio_write( &lio_err, finimsg, sizeof(finimsg)-1 ); |
259 |
#endif |
260 |
} /* openIsisFini */ |
261 |
|
262 |
|
263 |
void linit () /* called by cOpen() */ |
264 |
{ |
265 |
if ( init ) /* we're an .so -- implicitly initialized */ |
266 |
return; |
267 |
/* we're statically linked */ |
268 |
openIsisInit(); |
269 |
atexit( openIsisFini ); |
270 |
} |
271 |
|
272 |
|
273 |
|
274 |
|
275 |
int lio_open ( const char *name, int flags ) |
276 |
{ |
277 |
int fd = -1; |
278 |
int faillvl = (LIO_TRY == ((LIO_TRY|LIO_WR)&flags)) |
279 |
? (LOG_VERBOSE|ERR_BADF) : LOG_INFO|ERR_BADF /* LOG_IOERR too annoying ... */; |
280 |
|
281 |
if ( '&' == name[0] ) { |
282 |
int i = 1; |
283 |
fd = 0; |
284 |
while ( '0' <= name[i] && name[i] <= '9' ) |
285 |
fd = 10*fd + name[i++] - '0'; |
286 |
} |
287 |
|
288 |
#ifndef WIN32 |
289 |
if ( 0 > fd ) { |
290 |
int f = !(LIO_WR&flags) ? O_RDONLY |
291 |
: ((LIO_RD&flags) ? O_RDWR : O_WRONLY); |
292 |
if ( LIO_CREAT & flags && LIO_WR & flags ) f |= O_CREAT; |
293 |
if ( LIO_NBLK & flags ) f |= O_NONBLOCK; |
294 |
if ( LIO_SYNC & flags ) f |= O_SYNC; |
295 |
if ( LIO_TRUNC & flags ) f |= O_TRUNC; |
296 |
if ( ! (LIO_SEEK & flags) ) f |= O_APPEND; |
297 |
fd = open( name, f, 00664 ); /* let umask finetune */ |
298 |
if ( LIO_FD < fd ) { |
299 |
log_msg( LOG_ERROR, "got big fd %d", fd ); |
300 |
fd = -1; |
301 |
} |
302 |
if ( 0 > fd ) |
303 |
return log_msg( faillvl, "could not open '%s' %x", name, flags ); |
304 |
if ( LIO_FLOCK & flags ) { |
305 |
const char *lck = LIO_WR&flags ? "exclusive" : "shared"; |
306 |
/* |
307 |
we want an advisory lock, so that tail -f can read changes. |
308 |
SYSV/POSIX fcntl might be mandatory, depending on files mode bits |
309 |
(and filesystem). Therefore prefer BSD style flock, if available. |
310 |
|
311 |
under linux, flock is never mandatory, while lockf/fcntl may be. |
312 |
/usr/src/linux/Documentation/mandatory.txt |
313 |
moreover fcntl attempts to lock over NFS, which is a very feeble idea. |
314 |
*/ |
315 |
#ifndef LOCK_SH |
316 |
/* |
317 |
on solaris, LOCK_SH is declared only with the /usr/ucb/cc includes. |
318 |
their flock on fcntl emulation "must not be used in MT environments". |
319 |
*/ |
320 |
struct flock fl; |
321 |
memset( &fl, 0, sizeof(fl) ); |
322 |
fl.l_type = LIO_WR&flags ? F_WRLCK : F_RDLCK; |
323 |
fl.l_whence = SEEK_SET; |
324 |
fl.l_start = 0; |
325 |
fl.l_len = 1; /* 0 locks the whole file -- could be mandatory :( */ |
326 |
#endif |
327 |
log_msg( LOG_VERBOSE, "attempting %s lock on '%s'", lck, name ); |
328 |
if ( |
329 |
#ifndef LOCK_SH |
330 |
/* hmm .. at least this is "POSIX" */ |
331 |
fcntl( fd, LIO_WLOCK&flags ? F_SETLKW : F_SETLK, &fl ) |
332 |
#else |
333 |
flock( fd, |
334 |
(LIO_WR&flags ? LOCK_EX : LOCK_SH) | (LIO_WLOCK&flags ? 0 : LOCK_NB) ) |
335 |
#endif |
336 |
) { |
337 |
close( fd ); |
338 |
return log_msg( ERR_BADF, "could not get %s lock on '%s'", lck, name ); |
339 |
} |
340 |
} |
341 |
} else if ( -1 == fcntl( fd, F_GETFL ) ) /* check open */ |
342 |
return -ERR_BADF; |
343 |
#else |
344 |
if ( 0 > fd ) { |
345 |
HANDLE h; |
346 |
int acc = 0; |
347 |
int shr = FILE_SHARE_READ | (LIO_FLOCK&flags ? 0 : FILE_SHARE_WRITE); |
348 |
int cre = (LIO_CREAT & flags && LIO_WR & flags) |
349 |
? (LIO_TRUNC & flags) ? CREATE_ALWAYS : OPEN_ALWAYS |
350 |
: (LIO_TRUNC & flags) ? TRUNCATE_EXISTING : OPEN_EXISTING; |
351 |
int f = (LIO_WR&flags) ? FILE_ATTRIBUTE_ARCHIVE : FILE_ATTRIBUTE_NORMAL; |
352 |
|
353 |
if ( LIO_RD & flags ) acc |= GENERIC_READ; /* FILE_READ_DATA; */ |
354 |
if ( LIO_WR & flags ) acc |= GENERIC_WRITE; |
355 |
/* some routines like GetFileSize are "documented" to require |
356 |
GENERIC_READ/GENERIC_WRITE, so it may not be sufficient to use: |
357 |
(LIO_SEEK & flags) ? FILE_WRITE_DATA : FILE_APPEND_DATA */; |
358 |
/* if ( LIO_NBLK & flags ) f |= FILE_FLAG_OVERLAPPED; */ |
359 |
if ( LIO_SYNC & flags ) f |= FILE_FLAG_WRITE_THROUGH; |
360 |
f |= (LIO_SEEK & flags) ? FILE_FLAG_RANDOM_ACCESS |
361 |
: FILE_FLAG_SEQUENTIAL_SCAN; |
362 |
|
363 |
for ( fd=0; hand[fd]; ) |
364 |
if ( sizeof(hand)/sizeof(hand[0]) == ++fd ) |
365 |
return -EMFILE; |
366 |
h = CreateFile( name, acc, shr, 0, cre, f, 0 ); |
367 |
if ( INVALID_HANDLE_VALUE == h ) |
368 |
return log_msg( faillvl, "could not open '%s' %x", name, flags ); |
369 |
hand[fd] = h; |
370 |
if ( (LIO_WR & flags) && ! (LIO_SEEK & flags) ) |
371 |
SetFilePointer( hand[fd], 0, 0, FILE_END ); |
372 |
} else if ( (int)(sizeof(hand)/sizeof(hand[0])) <= fd || ! hand[fd] ) |
373 |
return -ERR_BADF; |
374 |
#endif |
375 |
fd |= flags & LIO_WANT; |
376 |
/* add status flags */ |
377 |
if ( LIO_RD & flags ) fd |= LIO_IN; |
378 |
if ( LIO_WR & flags ) fd |= LIO_OUT; |
379 |
return fd; |
380 |
} /* lio_open */ |
381 |
|
382 |
|
383 |
int lio_close ( int *file, int flags ) |
384 |
{ |
385 |
int fd = 0xffff & *file; |
386 |
int op = LIO_INOUT & *file; |
387 |
int cl = LIO_INOUT & flags; |
388 |
if ( ! op || ! cl ) |
389 |
return 0; |
390 |
if ( 0 > *file ) { /* probably failed open */ |
391 |
*file = 0; |
392 |
return 0; |
393 |
} |
394 |
*file &= ~cl; |
395 |
if ( op & ~cl ) { /* remains partly open */ |
396 |
int how = (LIO_IN & cl) ? SHUT_RD : SHUT_WR; |
397 |
if ( !(LIO_SOCK & fd) ) |
398 |
return 0; |
399 |
#ifndef WIN32 |
400 |
if ( ! shutdown( fd, how ) ) |
401 |
return 0; |
402 |
#else |
403 |
/* TODO */ |
404 |
#endif |
405 |
log_msg( LOG_SYSERR, "could not shutdown sock %d %d", fd, how ); |
406 |
return errno ? -errno : -1; |
407 |
} |
408 |
#ifndef WIN32 |
409 |
for ( fsync( fd ); close( fd ); errno = 0 ) |
410 |
if ( EINTR != errno ) { |
411 |
log_msg( LOG_SYSERR, "could not close file %d", fd ); |
412 |
return errno ? -errno : -1; |
413 |
} |
414 |
#else |
415 |
if ( (int)(sizeof(hand)/sizeof(hand[0])) <= fd ) |
416 |
return -ERR_BADF; |
417 |
CloseHandle( hand[fd] ); |
418 |
hand[fd] = 0; |
419 |
#endif |
420 |
return 0; |
421 |
} /* lio_close */ |
422 |
|
423 |
|
424 |
int lio_size ( int file ) |
425 |
{ |
426 |
if ( !((LIO_IN|LIO_OUT) & file) ) |
427 |
return log_msg( ERR_BADF, "*file 0x%x not open for stat", file ); |
428 |
{ |
429 |
int fd = LIO_FD & file; |
430 |
#ifndef WIN32 |
431 |
struct stat s; |
432 |
return fstat( fd, &s ) ? 0 : s.st_size; |
433 |
#else |
434 |
return GetFileSize( hand[fd], 0 ); |
435 |
#endif |
436 |
} |
437 |
} /* lio_size */ |
438 |
|
439 |
|
440 |
unsigned lio_time ( int file ) |
441 |
{ |
442 |
if ( !((LIO_IN|LIO_OUT) & file) ) |
443 |
return log_msg( ERR_BADF, "*file 0x%x not open for stat", file ); |
444 |
{ |
445 |
int fd = LIO_FD & file; |
446 |
#ifndef WIN32 |
447 |
struct stat s; |
448 |
return fstat( fd, &s ) ? 0 : (unsigned)s.st_mtime; |
449 |
#else |
450 |
FILETIME foo; /* time since 160101011200 UTC in hundred nanoseconds !!! */ |
451 |
ull bar; |
452 |
if ( ! GetFileTime( hand[fd], 0, 0, &foo ) ) |
453 |
return 0; |
454 |
bar = ((ull)foo.dwHighDateTime<<32 | (ull)foo.dwLowDateTime) / ULL(10000000); |
455 |
return (unsigned)(bar - ULL(11644473600)); |
456 |
#endif |
457 |
} |
458 |
} /* lio_size */ |
459 |
|
460 |
|
461 |
int lio_read ( int *file, void *buf, unsigned count ) |
462 |
{ |
463 |
if ( !(LIO_IN & *file) ) |
464 |
return log_msg( ERR_BADF, "*file 0x%x not open for reading", *file ); |
465 |
{ |
466 |
int fd = LIO_FD & *file; |
467 |
#ifndef WIN32 |
468 |
int got = read( fd, buf, count ); |
469 |
/* log_msg( LOG_ERROR, "read 0x%x got %d", *file, got ); */ |
470 |
if ( got ) |
471 |
return 0 < got ? got |
472 |
: unixio_ok( file, LIO_IN ) ? 0 |
473 |
: log_msg( LOG_IOERR, "could not read %d bytes from %d", count, fd ); |
474 |
else if ( LIO_SEEK & *file ) /* special EOF treatment */ |
475 |
return 0; |
476 |
else { |
477 |
lio_close( file, LIO_IN ); |
478 |
return -ERR_EOF; |
479 |
} |
480 |
#else |
481 |
DWORD got = 0; |
482 |
int ok = ReadFile( hand[fd], buf, count, &got, 0 ); |
483 |
return ok ? got |
484 |
: log_msg( LOG_IOERR, "could not read %d bytes from %d", count, fd ); |
485 |
#endif |
486 |
} |
487 |
} /* lio_read */ |
488 |
|
489 |
|
490 |
int lio_write ( int *file, const void *buf, unsigned count ) |
491 |
{ |
492 |
if ( !(LIO_OUT & *file) ) |
493 |
return log_msg( ERR_BADF, "*file 0x%x not open for writing", *file ); |
494 |
{ |
495 |
int fd = LIO_FD & *file; |
496 |
#ifndef WIN32 |
497 |
int got = write( fd, buf, count ); |
498 |
return 0 <= got ? got |
499 |
: unixio_ok( file, LIO_OUT ) ? 0 |
500 |
: log_msg( LOG_IOERR, "could not read %d bytes from %d", count, fd ); |
501 |
#else |
502 |
DWORD got = 0; |
503 |
int ok = WriteFile( hand[fd], buf, count, &got, 0 ); |
504 |
return ok ? got |
505 |
: log_msg( LOG_IOERR, "could not read %d bytes from %d", count, fd ); |
506 |
#endif |
507 |
} |
508 |
} /* lio_write */ |
509 |
|
510 |
|
511 |
|
512 |
int lio_seek ( int *file, int offset ) |
513 |
{ |
514 |
if ( !(LIO_INOUT & *file) ) |
515 |
return log_msg( ERR_BADF, "*file 0x%x not open", *file ); |
516 |
{ |
517 |
int fd = LIO_FD & *file; |
518 |
#ifndef WIN32 |
519 |
int got = (int)lseek( fd, offset, SEEK_SET ); |
520 |
return offset == got ? 0 |
521 |
: log_msg( LOG_IOERR, "could not seek to %d", offset ); |
522 |
#else |
523 |
return 0xffffffff /* "INVALID_SET_FILE_POINTER" */ |
524 |
== SetFilePointer( hand[fd], offset, 0, FILE_BEGIN ) |
525 |
? -1 : 0; |
526 |
#endif |
527 |
} |
528 |
} /* lio_seek */ |
529 |
|
530 |
|
531 |
int lio_pread ( int *file, void *buf, unsigned count, int offset ) |
532 |
{ |
533 |
if ( !(LIO_IN & *file) ) |
534 |
return log_msg( ERR_BADF, "*file 0x%x not open for reading", *file ); |
535 |
{ |
536 |
int fd = LIO_FD & *file; |
537 |
#ifndef WIN32 |
538 |
int got = pread( fd, buf, count, offset ); |
539 |
return 0 <= got ? got |
540 |
: unixio_ok( file, LIO_IN ) ? 0 |
541 |
: log_msg( LOG_IOERR, "could not read %d bytes from %d at %d", |
542 |
count, fd, offset ); |
543 |
#else |
544 |
return 0xffffffff /* "INVALID_SET_FILE_POINTER" */ |
545 |
== SetFilePointer( hand[fd], offset, 0, FILE_BEGIN ) |
546 |
? -1 : lio_read( file, buf, count ); |
547 |
#endif |
548 |
} |
549 |
} /* lio_pread */ |
550 |
|
551 |
|
552 |
int lio_pwrite ( int *file, const void *buf, unsigned count, int offset ) |
553 |
{ |
554 |
if ( !(LIO_OUT & *file) ) |
555 |
return log_msg( ERR_BADF, "*file 0x%x not open for writing", *file ); |
556 |
{ |
557 |
int fd = LIO_FD & *file; |
558 |
#ifndef WIN32 |
559 |
int got = pwrite( fd, buf, count, offset ); |
560 |
return 0 <= got ? got |
561 |
: unixio_ok( file, LIO_OUT ) ? 0 |
562 |
: log_msg( LOG_IOERR, "could not read %d bytes from %d at %d", |
563 |
count, fd, offset ); |
564 |
#else |
565 |
return 0xffffffff /* "INVALID_SET_FILE_POINTER" */ |
566 |
== SetFilePointer( hand[fd], offset, 0, FILE_BEGIN ) |
567 |
? -1 : lio_write( file, buf, count ); |
568 |
#endif |
569 |
} |
570 |
} /* lio_pwrite */ |
571 |
|
572 |
|
573 |
int lio_trunc ( int *file, int offset ) |
574 |
{ |
575 |
int fd = LIO_FD & *file; |
576 |
#ifndef WIN32 |
577 |
ftruncate( fd, offset ); |
578 |
#else |
579 |
/* SetFileValidData( hand[fd], offset ); XP only */ |
580 |
return 0xffffffff /* "INVALID_SET_FILE_POINTER" */ |
581 |
== SetFilePointer( hand[fd], offset, 0, FILE_BEGIN ) |
582 |
? -1 : SetEndOfFile( hand[fd] ) |
583 |
? 0 : log_msg( LOG_IOERR, "could not truncate %d to %d", fd, offset ); |
584 |
#endif |
585 |
return 0; |
586 |
} /* lio_trunc */ |
587 |
|
588 |
|
589 |
int lio_mmap ( int *file, void **map, int length ) |
590 |
{ |
591 |
if ( ! map ) |
592 |
return log_msg( ERR_INVAL, "no map" ); |
593 |
if ( 0 >= length && 0 >= (length = lio_size( *file )) ) { |
594 |
*map = 0; |
595 |
return length; |
596 |
} |
597 |
#ifndef WIN32 |
598 |
#if 0 /* solaris states it will round up itself, and linux does */ |
599 |
{ |
600 |
static int ps = 0; |
601 |
if ( !ps ) |
602 |
ps = sysconf(_SC_PAGESIZE); /* getpagesize(); */ |
603 |
} |
604 |
#endif |
605 |
if ( file ) { |
606 |
if ( *map ) |
607 |
return msync( *map, length, (LIO_SYNC & *file) |
608 |
? (MS_SYNC|MS_INVALIDATE) : MS_ASYNC ) |
609 |
? log_msg( LOG_IOERR, "msync" ) : 0; |
610 |
*map = mmap( 0, length, PROT_READ | ((LIO_OUT&*file) ? PROT_WRITE : 0), |
611 |
MAP_SHARED, LIO_FD&*file, 0 ); |
612 |
if ( MAP_FAILED != *map ) |
613 |
return length; |
614 |
*map = 0; |
615 |
return log_msg( ERR_NOMEM, "mmap failed on fd %08x", *file ); |
616 |
} |
617 |
if ( ! *map ) |
618 |
return log_msg( ERR_INVAL, "no *map" ); |
619 |
munmap( *map, length ); |
620 |
*map = 0; |
621 |
return 0; |
622 |
#else |
623 |
/* awfully complicated here |
624 |
-- need CreateFileMapping, MapViewOfFile |
625 |
stupid piece of shrott |
626 |
true mapping supported in NT family only, 9x copies to swap |
627 |
*/ |
628 |
*map = 0; |
629 |
return 0; |
630 |
#endif |
631 |
} /* lio_mmap */ |
632 |
|
633 |
|
634 |
int lio_slurp ( char **buf, int sz, const char *name, int opt ) |
635 |
{ |
636 |
int file = lio_open( name, LIO_RD|LIO_SEEK|(opt ? LIO_TRY : 0) ); |
637 |
int size; |
638 |
int ret = -ERR_NOMEM; |
639 |
char *p = *buf; |
640 |
/* log_msg( LOG_IOERR, "open '%s' = %d", name, file ); */ |
641 |
if ( 0 > file ) |
642 |
return file; |
643 |
size = lio_size( file ); |
644 |
if ( 0 >= size ) |
645 |
return size; |
646 |
if ( size > sz ) |
647 |
LOG_OTO( done, ( ERR_INVAL, "file '%.30s' too big: %d > %d", |
648 |
name, size, sz ) ); |
649 |
if ( ! p && !(p = mAlloc(size)) ) |
650 |
goto done; |
651 |
ret = lio_read( &file, p, size ); |
652 |
if ( size == ret ) |
653 |
*buf = p; |
654 |
else { |
655 |
if ( ret >= 0 ) |
656 |
ret = log_msg( ERR_IO, "OOPS! got %d of %d bytes from '%.30s'", |
657 |
ret, size, name ); |
658 |
if ( ! *buf ) |
659 |
free( p ); |
660 |
} |
661 |
done: |
662 |
lio_close( &file, LIO_INOUT ); |
663 |
return ret; |
664 |
} /* lio_slurp */ |
665 |
|
666 |
|
667 |
int log_msg ( int code, const char *fmt, ... ) |
668 |
{ |
669 |
static const char toolong[] = ": message too long !!!\n"; |
670 |
int err, level; |
671 |
va_list ap; |
672 |
va_start( ap, fmt ); |
673 |
#if 0 |
674 |
ret = sMsg( MSG_VA|code, fmt, ap ); |
675 |
make log_msg unbuffered, use sMsg for buffered logging |
676 |
#else |
677 |
if ( ! seterrlev( &err, &level, code ) && (LIO_OUT & lio_err) ) { |
678 |
char buf[4096]; |
679 |
int len = vsnprintf( (char*)buf, sizeof(buf), fmt, ap ); |
680 |
if ( 0 < len && len < (int)sizeof(buf) ) { |
681 |
buf[len++] = '\n'; |
682 |
lio_write( &lio_err, buf, len ); |
683 |
} else { |
684 |
lio_write( &lio_err, fmt, strlen(fmt) ); |
685 |
lio_write( &lio_err, toolong, sizeof(toolong)-1 ); |
686 |
} |
687 |
} |
688 |
#endif |
689 |
va_end( ap ); |
690 |
return -err; |
691 |
} /* log_msg */ |
692 |
|
693 |
|
694 |
void log_str ( LogLevel level, int *rec, const char **desc ) |
695 |
{ |
696 |
int occ = -1; |
697 |
int nmbrs = LSTRFIX(*rec); |
698 |
int *mbr = rec+1; |
699 |
char *base = (char*)rec; |
700 |
|
701 |
if ( level > log_lev || ! desc ) |
702 |
return; |
703 |
|
704 |
sMsg( 2, "record %.20s\n", *desc++ ); |
705 |
/* dump the fixed part (occ==-1) and each occurrence of repeated part. */ |
706 |
for ( ;/* occ < LSTROCC(*dst) */; ) { /* dump one part */ |
707 |
int i; |
708 |
for ( i=0; i<nmbrs; i++, mbr++ ) { /* dump one mbr */ |
709 |
if ( '\'' == *desc[i] ) |
710 |
sMsg( 2, "%3d.%2d %4.4s %.67s<\n", |
711 |
occ, i, desc[i], base+*mbr ); |
712 |
else |
713 |
sMsg( 2, "%3d.%2d %4.4s 0x%08x = %d\n", |
714 |
occ, i, desc[i], *mbr, *mbr ); |
715 |
} /* for mbrs */ |
716 |
|
717 |
if ( ++occ >= LSTROCC(*rec) ) |
718 |
break; |
719 |
if ( ! occ ) { /* was the fixed part, setup for repeated */ |
720 |
nmbrs = LSTRREP(*rec); |
721 |
desc += i; |
722 |
} |
723 |
} |
724 |
} /* log_str */ |
725 |
|
726 |
|
727 |
void log_hex ( LogLevel level, const void *mem, int len ) |
728 |
{ |
729 |
const char *p = (const char *)mem; |
730 |
char buf[82]; |
731 |
int i = 0; |
732 |
|
733 |
if ( level > log_lev ) |
734 |
return; |
735 |
for ( ; i<len; i+=16, p+=16 ) { |
736 |
int j = 0; |
737 |
int left = len -i; |
738 |
int pos = 10; |
739 |
sprintf( buf, "%08x ", i ); |
740 |
if ( left > 16 ) |
741 |
left = 16; |
742 |
for ( ; j < left; j++ ) { |
743 |
sprintf( buf+pos, "%02x", p[j] ); |
744 |
pos += 2; |
745 |
if ( 3 == j%4 ) { buf[pos++] = ' '; buf[pos++] = ' '; } |
746 |
} |
747 |
for ( ; j < 16; j++ ) { |
748 |
buf[pos++] = ' '; buf[pos++] = ' '; |
749 |
if ( 3 == j%4 ) { buf[pos++] = ' '; buf[pos++] = ' '; } |
750 |
} |
751 |
/* got 50 = 10 + 16*2 + 4*2 */ |
752 |
for ( j=0; j < left; j++ ) /* add up to 16 */ |
753 |
buf[pos++] = (0x60 & p[j]) ? p[j] : '.'; |
754 |
buf[pos++] = '\n'; |
755 |
buf[pos] = 0; |
756 |
sMsg( 2, "%.*s", pos, buf ); |
757 |
} |
758 |
} /* log_hex */ |
759 |
|
760 |
|
761 |
/* ************************************************************ |
762 |
public functions |
763 |
*/ |
764 |
int timeUpd ( Tm *tm ) |
765 |
{ |
766 |
Tm o; |
767 |
#ifdef WIN32 |
768 |
struct _timeb tb; |
769 |
_ftime( &tb ); |
770 |
if ( !tm ) |
771 |
return tb.time; |
772 |
o = *tm; |
773 |
tm->millis = tb.time*LLL(1000) + tb.millitm; |
774 |
#else |
775 |
struct timeval tv; |
776 |
gettimeofday( &tv, 0 ); |
777 |
if ( !tm ) |
778 |
return tv.tv_sec; |
779 |
o = *tm; |
780 |
tm->millis = tv.tv_sec*LLL(1000) + tv.tv_usec/1000; |
781 |
#endif |
782 |
return (int)(tm->millis - o.millis); |
783 |
} /* timeUpd */ |
784 |
|
785 |
|
786 |
static int timeLoc ( struct tm *t, Tm *tm ) |
787 |
{ |
788 |
Tm x; |
789 |
time_t tt; |
790 |
if ( !tm || !tm->millis ) |
791 |
timeUpd( tm ? tm : (tm = &x) ); |
792 |
tt = (time_t)(tm->millis / 1000); |
793 |
#ifdef WIN32 /* did I mention it's not threadsafe ? */ |
794 |
*t = *localtime( &tt ); |
795 |
#else |
796 |
localtime_r( &tt, t ); |
797 |
#endif |
798 |
return (int)(tm->millis % 1000); |
799 |
} /* timeLoc */ |
800 |
|
801 |
char *timeGtf ( char *buf, Tm *tm ) |
802 |
{ |
803 |
struct tm t; |
804 |
timeLoc( &t, tm ); |
805 |
snprintf( buf, 15, "%04u%02u%02u%02u%02u%02u", |
806 |
1900+t.tm_year, 1+t.tm_mon, t.tm_mday, |
807 |
t.tm_hour, t.tm_min, t.tm_sec ); |
808 |
buf[14] = 0; |
809 |
return buf; |
810 |
} /* timeGtf */ |
811 |
|
812 |
|
813 |
char *timeGtfm ( char *buf, Tm *tm ) |
814 |
{ |
815 |
struct tm t; |
816 |
int millis = timeLoc( &t, tm ); |
817 |
snprintf( buf, 19, "%04u%02u%02u%02u%02u%02u%03u", |
818 |
1900+t.tm_year, 1+t.tm_mon, t.tm_mday, |
819 |
t.tm_hour, t.tm_min, t.tm_sec, millis ); |
820 |
buf[18] = 0; |
821 |
return buf; |
822 |
} /* timeGtfm */ |
823 |
|
824 |
|
825 |
void timeSleep ( Tm *tm ) |
826 |
{ |
827 |
#ifdef WIN32 |
828 |
Sleep( tm->millis ); |
829 |
#else |
830 |
struct timespec ts; |
831 |
ts.tv_sec = tm->millis / 1000; |
832 |
ts.tv_nsec = 1000000 * (int)(tm->millis % 1000); |
833 |
nanosleep( &ts, 0 ); |
834 |
#endif |
835 |
} /* timeSleep */ |
836 |
|
837 |
|
838 |
int ioStream ( Ios *s, int op ) |
839 |
{ |
840 |
Buf *b; |
841 |
switch ( op ) { |
842 |
case LIO_SSIZE: |
843 |
return sizeof(Ios); |
844 |
case LIO_SOPEN: |
845 |
s->pos = s->b.fill = s->b.done = 0; |
846 |
case LIO_SCLOSE: |
847 |
s->file &= ~LIO_INOUT; |
848 |
return 0; |
849 |
case LIO_SPURGE: |
850 |
if ( !(LIO_IN & s->file) ) |
851 |
return -ERR_BADF; |
852 |
/* try to get 4k chunk */ |
853 |
b = /* s->alt ? s->alt : */ &s->b; |
854 |
if ( LIO_BUFSIZ/2 < b->fill && b->done ) { /* move contents downwards */ |
855 |
if ( b->done < b->fill ) |
856 |
memmove( b->c, b->c + b->done, b->fill - b->done ); |
857 |
s->pos += b->done; |
858 |
b->fill -= b->done; |
859 |
b->done = 0; |
860 |
} |
861 |
return LIO_BUFSIZ <= b->fill ? 0 |
862 |
: LIO_BUFSIZ/2 < b->fill ? LIO_BUFSIZ - b->fill |
863 |
: LIO_BUFSIZ/2; |
864 |
case LIO_SFILL: |
865 |
case LIO_SFLUSH: |
866 |
s->file &= ~LIO_INOUT; |
867 |
} |
868 |
return -ERR_EOF; |
869 |
} /* ioStream */ |
870 |
|
871 |
|
872 |
int ioStdio ( Ios *s, int op ) |
873 |
{ |
874 |
Buf *b; |
875 |
int ret; |
876 |
switch ( op ) { |
877 |
case LIO_SOPEN: |
878 |
ioStream( s, op ); |
879 |
if ( 0 < (ret = lio_open( s->name, s->file )) ) |
880 |
s->file = ret; |
881 |
return ret; |
882 |
case LIO_SCLOSE: |
883 |
if ( LIO_OUT & s->file ) |
884 |
s->func( s, LIO_SFLUSH ); |
885 |
/* don't close borrowed fd */ |
886 |
return '&' == *s->name ? 0 : lio_close( &s->file, LIO_INOUT ); |
887 |
case LIO_SFILL: |
888 |
if ( !(LIO_IN & s->file) ) |
889 |
return -ERR_BADF; |
890 |
if ( 0 < (ret = ioStream( s, LIO_SPURGE )) |
891 |
&& 0 <= (ret = lio_read( &s->file, s->b.c + s->b.fill, ret )) |
892 |
) |
893 |
s->b.fill += ret; |
894 |
log_msg( LOG_ALL, "LIO_FILL: got %d now %d", ret, s->b.fill ); |
895 |
return ret; |
896 |
case LIO_SFLUSH: |
897 |
if ( !(LIO_OUT & s->file) ) |
898 |
return -ERR_BADF; |
899 |
b = /* s->alt ? s->alt : */ &s->b; |
900 |
if ( b->fill <= b->done ) |
901 |
return 0; |
902 |
if ( 0 < (ret = lio_write( &s->file, b->c + b->done, b->fill - b->done )) ) |
903 |
b->done += ret; |
904 |
if ( b->fill == b->done ) { |
905 |
s->pos += b->done; |
906 |
b->fill = b->done = 0; |
907 |
} else if ( b->done && LIO_BUFSIZ/2 < b->fill ) |
908 |
ioStream( s, LIO_SPURGE ); |
909 |
return ret; |
910 |
} |
911 |
return ioStream( s, op ); |
912 |
} /* ioStdio */ |
913 |
|
914 |
|
915 |
int sMsg ( int to, const char *fmt, ... ) |
916 |
{ |
917 |
SESDECL |
918 |
int code = (LOG_MASK|ERR_MASK) & to, err, level; |
919 |
int fd = LIO_FD & to; /* stream id uses same mask as system fd */ |
920 |
Ios *o = ses->io[ fd ? fd : code ? 2 : 1 ]; |
921 |
va_list ap; |
922 |
int space; |
923 |
int ret; |
924 |
|
925 |
if ( LSES_FILE_MAX <= fd || ! o ) |
926 |
return -1; |
927 |
|
928 |
if ( seterrlev( &err, &level, code ) ) /* logging */ |
929 |
return -err; |
930 |
|
931 |
/* core fprintf */ |
932 |
if ( LIO_BUFSIZ/2 < o->b.fill ) |
933 |
LIO_FLUSH( o ); |
934 |
space = LIO_BUFSIZ - o->b.fill; |
935 |
va_start( ap, fmt ); |
936 |
ret = vsnprintf( (char*)o->b.c + o->b.fill, space, fmt, |
937 |
(MSG_VA & to) ? va_arg( ap, va_list ) : ap ); |
938 |
va_end( ap ); |
939 |
if ( ret < 0 || space < ret ) /* outta space */ |
940 |
ret = space; |
941 |
o->b.fill += ret; |
942 |
if ( ! code ) |
943 |
return ret; |
944 |
|
945 |
/* logging afterburner */ |
946 |
if ( LOG_SYSERR == level || LOG_IOERR == level ) { |
947 |
int len; |
948 |
char *syserr = |
949 |
#ifndef WIN32 |
950 |
strerror(errno); |
951 |
#else |
952 |
0; |
953 |
char buf[256] = ""; |
954 |
if ( |
955 |
FormatMessage( |
956 |
FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, |
957 |
0, GetLastError(), 0, buf, sizeof(buf)-1, 0 ) |
958 |
) |
959 |
syserr = buf; |
960 |
buf[ sizeof(buf)-1 ] = 0; |
961 |
#endif |
962 |
if ( syserr ) { |
963 |
len = strlen( syserr ); |
964 |
if ( o->b.fill+4+len < LIO_BUFSIZ ) { |
965 |
memcpy( o->b.c + o->b.fill, "\n\t: ", 4 ); |
966 |
memcpy( o->b.c + o->b.fill+4, syserr, len ); |
967 |
o->b.fill += 4+len; |
968 |
} |
969 |
} |
970 |
} |
971 |
if ( o->b.fill < LIO_BUFSIZ ) |
972 |
o->b.c[ o->b.fill++ ] = '\n'; |
973 |
if ( log_flush ) |
974 |
LIO_FLUSH( o ); |
975 |
return -err; |
976 |
} /* sMsg */ |
977 |
|
978 |
|
979 |
int sGets ( int fd, char **ptr, char delim ) |
980 |
{ |
981 |
SESDECL |
982 |
Ios *s = ses->io[ LIO_FD & fd ]; |
983 |
|
984 |
if ( s->b.fill > s->b.done || 0 < LIO_FILL( s ) ) for (;;) { |
985 |
char *f = memchr( s->b.c + s->b.done, delim, s->b.fill - s->b.done ); |
986 |
int len; |
987 |
log_msg( LOG_ALL, "sGets: done %d fill %d", s->b.done, s->b.fill ); |
988 |
f = memchr( s->b.c + s->b.done, delim, s->b.fill - s->b.done ); |
989 |
if ( ! f ) { |
990 |
if ( (s->b.fill < LIO_BUFSIZ || s->b.done) /* buffer avail */ |
991 |
&& (LIO_IN & s->file) /* file open */ |
992 |
&& 0 < LIO_FILL( s ) |
993 |
) |
994 |
continue; |
995 |
else if ( s->b.done >= s->b.fill ) |
996 |
break; |
997 |
f = (char*)s->b.c + s->b.fill; |
998 |
} |
999 |
len = f - (*ptr = (char*)s->b.c + s->b.done); |
1000 |
s->b.done += len + 1; |
1001 |
if ( len && '\n' == delim && '\r' == (*ptr)[len-1] ) |
1002 |
len--; |
1003 |
return len; |
1004 |
} |
1005 |
*ptr = 0; |
1006 |
return -1; |
1007 |
} /* sGets */ |
1008 |
|
1009 |
|
1010 |
void cLog ( int level, const char *filename ) |
1011 |
{ |
1012 |
if ( 0 > level ) /* no change */ |
1013 |
; |
1014 |
else if ( LOG_LEVELS > level ) /* by basic number */ |
1015 |
level <<= LOG_SHIFT; |
1016 |
else if ( 'z' >= level ) { /* by ascii value */ |
1017 |
switch ( level ) { |
1018 |
case '-': level = LOG_NOCHANGE; break; |
1019 |
case 'o': level = LOG_OFF; break; |
1020 |
case 'f': level = LOG_FATAL; break; |
1021 |
case 's': level = LOG_IOERR /*LOG_SYSERR*/; break; |
1022 |
default: |
1023 |
if ( '0' <= level && level <= '9' ) { |
1024 |
level = (level - '0')<<LOG_SHIFT; |
1025 |
break; |
1026 |
} |
1027 |
case 'e': level = LOG_ERROR; break; |
1028 |
case 'w': level = LOG_WARN; break; |
1029 |
case 'i': level = LOG_INFO; break; |
1030 |
case 'v': level = LOG_VERBOSE; break; |
1031 |
case 'd': level = LOG_DEBUG; break; |
1032 |
case 't': level = LOG_TRACE; break; |
1033 |
case 'a': level = LOG_ALL; break; |
1034 |
} |
1035 |
} |
1036 |
if ( level >= 0 ) |
1037 |
log_lev = (LogLevel)level; |
1038 |
(void)filename; |
1039 |
/* TODO if ( filename ) |
1040 |
sOpen( cOpen(0), "2>"filename, 0, ioStdio ) */ |
1041 |
} /* cLog */ |