sq_ipc.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. #ifdef __cplusplus
  2. extern "C" {
  3. #endif
  4. //#ifdef USE_IPC
  5. #include "squirrel.h"
  6. #include <string.h>
  7. #include <stdio.h>
  8. #include <stdlib.h> /* for malloc */
  9. #include <assert.h> /* for a few sanity tests */
  10. #include <sqstdio.h>
  11. #include <sqstdfile.h>
  12. //#include "FileLock.h"
  13. /* check for POSIX */
  14. #if defined( unix ) || defined( __unix ) || defined( __unix__ ) || \
  15. (defined( __APPLE__ ) && defined( __MACH__ )) || \
  16. HAVE_UNISTD_H
  17. # include <unistd.h>
  18. # if defined( _POSIX_VERSION ) && _POSIX_VERSION >= 200112L
  19. # define HAVE_FLOCK
  20. #include <stddef.h>
  21. #include <errno.h>
  22. #include <unistd.h>
  23. #include <fcntl.h>
  24. typedef off_t ipc_flock_off_t;
  25. # endif
  26. #endif
  27. /* check for Windows */
  28. #if !defined( HAVE_FLOCK ) && \
  29. defined( _WIN32 ) && !defined( __CYGWIN__ )
  30. # define HAVE_FLOCK
  31. #include <stddef.h>
  32. #include <ctype.h>
  33. #define WIN32_LEAN_AND_MEAN
  34. #include <windows.h>
  35. #include <io.h>
  36. typedef ULONGLONG ipc_flock_off_t;
  37. #endif
  38. static int ipc_err( char const* file, int line, char const* func,
  39. int code ) {
  40. if( code != 0 ) {
  41. if( func != NULL )
  42. fprintf( stderr, "[%s:%d] error return (%d) in function '%s'\n",
  43. file, line, code, func );
  44. else
  45. fprintf( stderr, "[%s:%d]: error return (%d)\n",
  46. file, line, code );
  47. fflush( stderr );
  48. }
  49. return code;
  50. }
  51. #ifndef NDEBUG
  52. # if (defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 199901L) || \
  53. defined( __GNUC__ ) || defined( __clang__ )
  54. # define IPC_ERR( code ) (ipc_err( __FILE__, __LINE__, __func__, (int)(code) ))
  55. # elif defined( _MSC_VER ) && _MSC_VER >= 1100L
  56. # define IPC_ERR( code ) (ipc_err( __FILE__, __LINE__, __FUNCTION__, (int)(code) ))
  57. # else
  58. # define IPC_ERR( code ) (ipc_err( __FILE__, __LINE__, NULL, (int)(code) ))
  59. # endif
  60. #else
  61. # define IPC_ERR( code ) ((int)(code))
  62. #endif
  63. #define IPC_EINTR( _rv, _call ) \
  64. do { \
  65. _rv = _call; \
  66. } while( _rv < 0 && errno == EINTR )
  67. #if 0
  68. //memmap
  69. #include <stddef.h>
  70. #include <limits.h>
  71. #include <string.h>
  72. #include <errno.h>
  73. #include <sys/types.h>
  74. #include <sys/mman.h>
  75. #include <sys/stat.h>
  76. #include <unistd.h>
  77. #include <fcntl.h>
  78. typedef off_t ipc_mmap_off_t;
  79. #define MEMFILE_R 1
  80. #define MEMFILE_W 2
  81. #define MEMFILE_RW (MEMFILE_R|MEMFILE_W)
  82. typedef struct {
  83. void* addr;
  84. size_t len;
  85. } ipc_mmap_handle;
  86. static void* ipc_mmap_addr( ipc_mmap_handle* h ) {
  87. return h->addr;
  88. }
  89. static size_t ipc_mmap_size( ipc_mmap_handle* h ) {
  90. return h->len;
  91. }
  92. static void ipc_mmap_error( char* buf, size_t len, int code ) {
  93. if( len > 0 && strerror_r( code, buf, len ) != (int)0 ) {
  94. strncpy( buf, "unknown error", len-1 );
  95. buf[ len-1 ] = '\0';
  96. }
  97. }
  98. static size_t ipc_mmap_pagesize( void ) {
  99. long result = sysconf( _SC_PAGESIZE );
  100. if( result < 1 )
  101. result = 4096;
  102. return (size_t)result;
  103. }
  104. static int ipc_mmap_open( ipc_mmap_handle* h, char const* name,
  105. int mode, off_t offset, size_t size ) {
  106. int fd, oflags = 0, mmflags = 0;
  107. if( (mode & MEMFILE_RW) == MEMFILE_RW ) {
  108. oflags = O_RDWR;
  109. mmflags = PROT_READ | PROT_WRITE;
  110. } else if( mode & MEMFILE_R ) {
  111. oflags = O_RDONLY;
  112. mmflags = PROT_READ;
  113. } else if( mode & MEMFILE_W ) {
  114. oflags = O_RDWR;
  115. mmflags = PROT_WRITE;
  116. }
  117. #ifdef O_CLOEXEC
  118. oflags |= O_CLOEXEC;
  119. #endif
  120. fd = open( name, oflags );
  121. if( fd < 0 )
  122. return IPC_ERR( errno );
  123. h->len = size;
  124. if( size == 0 ) { /* figure out its size */
  125. struct stat buf;
  126. if( fstat( fd, &buf ) < 0 ) {
  127. int saved_errno = errno;
  128. close( fd );
  129. return IPC_ERR( saved_errno );
  130. }
  131. if( buf.st_size < offset ) {
  132. close( fd );
  133. return IPC_ERR( EINVAL );
  134. }
  135. if( buf.st_size - offset > (size_t)-1 )
  136. h->len = (size_t)-1;
  137. else
  138. h->len = buf.st_size - offset;
  139. }
  140. /* create mmap */
  141. h->addr = mmap( NULL, h->len, mmflags, MAP_SHARED, fd, offset );
  142. if( h->addr == MAP_FAILED ) {
  143. int saved_errno = errno;
  144. close( fd );
  145. return IPC_ERR( saved_errno );
  146. }
  147. close( fd ); /* we don't need it anymore! */
  148. return 0;
  149. }
  150. static int ipc_mmap_close( ipc_mmap_handle* h ) {
  151. int rv = munmap( h->addr, h->len );
  152. if( rv < 0 )
  153. return IPC_ERR( errno );
  154. return 0;
  155. }
  156. #if defined( _POSIX_SYNCHRONIZED_IO ) && _POSIX_SYNCHRONIZED_IO > 0
  157. # define IPC_MMAP_HAVE_FLUSH
  158. static int ipc_mmap_flush( ipc_mmap_handle* h, size_t pos ) {
  159. int rv = msync( h->addr, pos, MS_ASYNC|MS_INVALIDATE );
  160. if( rv < 0 )
  161. return IPC_ERR( errno );
  162. return 0;
  163. }
  164. #endif
  165. //#ifdef HAVE_MMAP
  166. #define NAME "ipc.mmap"
  167. typedef struct {
  168. ipc_mmap_handle h; /* platform specific data */
  169. /* extra management info: */
  170. char is_valid;
  171. } sq_mmap_handle;
  172. static SQRESULT mmap_pusherror( HSQUIRRELVM v, int code ) {
  173. char buf[ IPC_MAXERRMSG ];
  174. ipc_mmap_error( buf, sizeof( buf ), code );
  175. return sq_throwerror(v, _SC("%s"), buf);
  176. }
  177. static SQRESULT sq_mmap_close(HSQUIRRELVM v)
  178. {
  179. SQ_FUNC_VARS(v);
  180. GET_file_INSTANCE();
  181. int rv = 0;
  182. if(!self->is_valid) return sq_throwerror(v, _SC("attempt to use invalid mmap object"));
  183. rv = ipc_mmap_close( &self->h );
  184. if( rv != 0 ) return mmap_pusherror(v, rv);
  185. self->is_valid = 0;
  186. sq_pushbool(v, SQTrue);
  187. return 1;
  188. }
  189. static SQRESULT sq_mmap_gc( HSQUIRRELVM v ) {
  190. l_mmap_handle* h = lua_touserdata( L, 1 );
  191. if( h->is_valid )
  192. ipc_mmap_close( &h->h );
  193. return 0;
  194. }
  195. #ifdef IPC_MMAP_HAVE_FLUSH
  196. static SQRESULT sq_mmap_flush( HSQUIRRELVM v ) {
  197. SQ_FUNC_VARS(v);
  198. GET_file_INSTANCE();
  199. SQ_GET_INTEGER(v, 2, pos);
  200. int rv = 0;
  201. if( !self->is_valid ) return sq_throwerror(v, _SC("attempt to use invalid mmap object"));
  202. rv = ipc_mmap_flush( &self->h, pos );
  203. if( rv != 0 ) return mmap_pusherror(v, rv);
  204. sq_pushbool(v, SQTrue);
  205. return 1;
  206. }
  207. #endif
  208. static SQRESULT getMmapMode(HSQUIRRELVM v, const char *mode)
  209. {
  210. if(mode)
  211. {
  212. switch(mode[0])
  213. {
  214. case 'r':
  215. if(mode[1] == 'w') return MEMFILE_RW;
  216. return MEMFILE_R;
  217. break;
  218. case 'w':
  219. return MEMFILE_W;
  220. break;
  221. }
  222. }
  223. return sq_throwerror(v, _SC("invalid parameter mode '%s'"), mode);;
  224. }
  225. static SQRESULT sq_mmap_open(HSQUIRRELVM v)
  226. {
  227. SQ_FUNC_VARS(v);
  228. GET_file_INSTANCE();
  229. SQ_GET_STRING(v, 2, name);
  230. SQ_GET_STRING(v, 3, mode);
  231. SQ_OPT_INTEGER(v, 4, offset, 0);
  232. SQ_OPT_INTEGER(v, 5, nbytes, 0);
  233. int mmode = getMmapMode(v, mode);
  234. if(mmode < 0) return mmode;
  235. ipc_mmap_off_t offset = offset;
  236. size_t size = nbytes;
  237. l_mmap_handle* h = lua_newuserdata( L, sizeof( *h ) );
  238. int rv = 0;
  239. h->is_valid = 0;
  240. luaL_getmetatable( L, NAME );
  241. lua_setmetatable( L, -2 );
  242. rv = ipc_mmap_open( &h->h, name, mode, offset, size );
  243. if( rv != 0 ) return mmap_pusherror( L, rv );
  244. h->is_valid = 1;
  245. sq_pushcfunction(v, sq_mmap_close );
  246. #ifdef IPC_MMAP_HAVE_FLUSH
  247. sq_pushcfunction(v, l_mmap_flush );
  248. memfile_new( L, ipc_mmap_addr( &h->h ), ipc_mmap_size( &h->h ),
  249. mode, -3, -2, -1 );
  250. #else
  251. memfile_new( L, ipc_mmap_addr( &h->h ), ipc_mmap_size( &h->h ),
  252. mode, -2, -1, 0 );
  253. #endif
  254. return 1;
  255. }
  256. //#endif // HAVE_MMAP
  257. #endif // 0
  258. //file lock
  259. static void ipc_flock_error( char* buf, size_t len, int code ) {
  260. #ifdef _WIN32
  261. if( len > 0 ) {
  262. if( 0 == FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM |
  263. FORMAT_MESSAGE_IGNORE_INSERTS,
  264. NULL,
  265. code,
  266. 0,
  267. buf,
  268. len,
  269. NULL ) ) {
  270. strncpy( buf, "unknown error", len-1 );
  271. buf[ len-1 ] = '\0';
  272. } else { /* Windows puts an extra newline in there! */
  273. size_t n = strlen( buf );
  274. while( n > 0 && isspace( (unsigned char)buf[ --n ] ) )
  275. buf[ n ] = '\0';
  276. }
  277. }
  278. #else
  279. if( len > 0 && strerror_r( code, buf, len ) != (int)0 ) {
  280. strncpy( buf, "unknown error", len-1 );
  281. buf[ len-1 ] = '\0';
  282. }
  283. #endif // _WIN32
  284. }
  285. static int ipc_flock_lock( FILE* f, int is_wlock, int* could_lock,
  286. ipc_flock_off_t start,
  287. ipc_flock_off_t len ) {
  288. #ifdef _WIN32
  289. HANDLE fh = (HANDLE)_get_osfhandle( _fileno( f ) );
  290. DWORD flags = is_wlock ? LOCKFILE_EXCLUSIVE_LOCK : 0;
  291. DWORD lenlo = (DWORD)len, lenhi = (DWORD)(len >> 32);
  292. OVERLAPPED ov;
  293. if( fh == (HANDLE)INVALID_HANDLE_VALUE ) return IPC_ERR( ERROR_INVALID_HANDLE );
  294. if( could_lock != NULL ) flags |= LOCKFILE_FAIL_IMMEDIATELY;
  295. ov.Offset = (DWORD)start;
  296. ov.OffsetHigh = (DWORD)(start >> 32);
  297. ov.hEvent = NULL;
  298. if( len == 0 ) lenhi = lenlo = (DWORD)-1;
  299. if( !LockFileEx( fh, flags, 0, lenlo, lenhi, &ov ) ) {
  300. int code = GetLastError();
  301. if( could_lock != NULL && (code == ERROR_LOCK_VIOLATION || code == ERROR_IO_PENDING) )
  302. {
  303. *could_lock = 0;
  304. return 0;
  305. }
  306. return IPC_ERR( code );
  307. }
  308. #else
  309. int rv = 0;
  310. int fd = fileno( f );
  311. int op = could_lock != NULL ? F_SETLK : F_SETLKW;
  312. struct flock fl;
  313. fl.l_type = is_wlock ? F_WRLCK : F_RDLCK;
  314. fl.l_whence = SEEK_SET;
  315. fl.l_start = start;
  316. fl.l_len = len;
  317. IPC_EINTR( rv, fcntl( fd, op, &fl ) );
  318. if( rv < 0 ) {
  319. if( could_lock != NULL && (errno == EACCES || errno == EAGAIN) ) {
  320. *could_lock = 0;
  321. return 0;
  322. }
  323. return IPC_ERR( errno );
  324. }
  325. #endif
  326. if( could_lock != NULL ) *could_lock = 1;
  327. return 0;
  328. }
  329. static int ipc_flock_unlock( FILE* f, ipc_flock_off_t start,
  330. ipc_flock_off_t len ) {
  331. #ifdef _WIN32
  332. HANDLE fh = (HANDLE)_get_osfhandle( _fileno( f ) );
  333. DWORD lenlo = (DWORD)len, lenhi = (DWORD)(len >> 32);
  334. DWORD offlo = (DWORD)start, offhi = (DWORD)(start >> 32);
  335. if( fh == (HANDLE)INVALID_HANDLE_VALUE ) return IPC_ERR( ERROR_INVALID_HANDLE );
  336. if( len == 0 ) lenhi = lenlo = (DWORD)-1;
  337. if( !UnlockFile( fh, offlo, offhi, lenlo, lenhi ) ) return IPC_ERR( GetLastError() );
  338. #else
  339. struct flock fl;
  340. fl.l_type = F_UNLCK;
  341. fl.l_whence = SEEK_SET;
  342. fl.l_start = start;
  343. fl.l_len = len;
  344. if( fcntl( fileno( f ), F_SETLK, &fl ) < 0 ) return IPC_ERR( errno );
  345. #endif // _WIN32
  346. return 0;
  347. }
  348. /* maximum expected length of error messages */
  349. #define IPC_MAXERRMSG 200
  350. static SQRESULT flock_pusherror(HSQUIRRELVM v, int code) {
  351. char buf[ IPC_MAXERRMSG ];
  352. ipc_flock_error( buf, sizeof( buf ), code );
  353. return sq_throwerror(v, _SC("%s"), buf);
  354. }
  355. static void invalidate_input_buffer( FILE* f ) {
  356. /* Linux (and apparently many other implementations) discard
  357. * unread characters from the input buffer if fflush is called on
  358. * an input file, but this is not guaranteed by ISO C. */
  359. fflush( f );
  360. /* This should also invalidate the input buffer unless the
  361. * implementation checks for that specific case. */
  362. fseek( f, 0, SEEK_CUR );
  363. /* If both methods don't work, we are out of luck. But using
  364. * low-level file locking with buffered IO is a bad idea
  365. * anyway! */
  366. }
  367. SQ_OPT_STRING_STRLEN();
  368. #define GET_file_INSTANCE() SQ_GET_INSTANCE(v, 1, SQFile, SQSTD_FILE_TYPE_TAG) \
  369. if(self == NULL) return sq_throwerror(v, _SC("file object already closed"));
  370. static SQRESULT getFlockMode(HSQUIRRELVM v, const char *mode)
  371. {
  372. if(mode)
  373. {
  374. switch(mode[0])
  375. {
  376. case 'r':
  377. if(mode[1] == 'w') return 1;
  378. return 0;
  379. break;
  380. case 'w':
  381. return 1;
  382. break;
  383. }
  384. }
  385. return sq_throwerror(v, _SC("invalid parameter mode '%s'"), mode);;
  386. }
  387. static SQRESULT sq_FileLock_lock0(HSQUIRRELVM v, int isTry){
  388. SQ_FUNC_VARS(v);
  389. GET_file_INSTANCE();
  390. SQ_GET_STRING(v, 2, mode);
  391. SQ_OPT_INTEGER(v, 3, offset, 0);
  392. SQ_OPT_INTEGER(v, 4, nbytes, 0);
  393. FILE *fp = (FILE*)self->GetHandle();
  394. int is_wlock = getFlockMode(v, mode);
  395. if(is_wlock < 0) return is_wlock;
  396. ipc_flock_off_t start = offset;
  397. ipc_flock_off_t len = nbytes;
  398. int could_lock = isTry ? 0 : 1;
  399. int rv = ipc_flock_lock(fp, (isTry ? is_wlock : 0), &could_lock, start, len);
  400. if( rv != 0 ) return flock_pusherror(v, rv);
  401. /* try to flush input buffer */
  402. if(could_lock) invalidate_input_buffer(fp);
  403. sq_pushbool(v, could_lock);
  404. return 1;
  405. }
  406. static SQRESULT sq_FileLock_lock(HSQUIRRELVM v){
  407. return sq_FileLock_lock0(v, 0);
  408. }
  409. static SQRESULT sq_FileLock_trylock(HSQUIRRELVM v){
  410. return sq_FileLock_lock0(v, 1);
  411. }
  412. static SQRESULT sq_FileLock_unlock(HSQUIRRELVM v){
  413. SQ_FUNC_VARS(v);
  414. GET_file_INSTANCE();
  415. SQ_OPT_INTEGER(v, 2, offset, 0);
  416. SQ_OPT_INTEGER(v, 3, nbytes, 0);
  417. FILE *fp = (FILE*)self->GetHandle();
  418. ipc_flock_off_t start = offset;
  419. ipc_flock_off_t len = nbytes;
  420. int rv = 0;
  421. fflush(fp); /* flush output buffer */
  422. rv = ipc_flock_unlock(fp, start, len);
  423. if( rv != 0 ) return flock_pusherror(v, rv);
  424. sq_pushbool(v, SQTrue);
  425. return 1;
  426. }
  427. #define _DECL_FILELOCK_FUNC(name,nparams,pmask) {_SC(#name),sq_FileLock_##name,nparams,pmask}
  428. static SQRegFunction FileLock_obj_funcs[]={
  429. _DECL_FILELOCK_FUNC(lock, -2, _SC("xsii")),
  430. _DECL_FILELOCK_FUNC(trylock, -2, _SC("xsii")),
  431. _DECL_FILELOCK_FUNC(unlock, -1, _SC("x")),
  432. {0,0}
  433. };
  434. #undef _DECL_FILELOCK_FUNC
  435. /* This defines a function that opens up your library. */
  436. SQRESULT sqext_register_ipc (HSQUIRRELVM v) {
  437. //add a namespace FileLock
  438. sq_pushstring(v, SQSTD_FILE_CLASS_TYPE_TAG, -1);
  439. if(sq_getonregistrytable(v) == SQ_OK){
  440. sq_insert_reg_funcs(v, FileLock_obj_funcs);
  441. sq_poptop(v);
  442. return SQ_OK;
  443. }
  444. return sq_throwerror(v, _SC("file class not found"));
  445. }
  446. #ifdef __cplusplus
  447. }
  448. //#endif //USE_IPC
  449. #endif