1 /*
2  * fcntl.c - fcntl interface
3  *
4  *   Copyright (c) 2000-2020  Shiro Kawai  <shiro@acm.org>
5  *
6  *   Redistribution and use in source and binary forms, with or without
7  *   modification, are permitted provided that the following conditions
8  *   are met:
9  *
10  *   1. Redistributions of source code must retain the above copyright
11  *      notice, this list of conditions and the following disclaimer.
12  *
13  *   2. Redistributions in binary form must reproduce the above copyright
14  *      notice, this list of conditions and the following disclaimer in the
15  *      documentation and/or other materials provided with the distribution.
16  *
17  *   3. Neither the name of the authors nor the names of its contributors
18  *      may be used to endorse or promote products derived from this
19  *      software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27  *   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28  *   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29  *   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30  *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31  *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #define _GNU_SOURCE  /* for Linux, this enables additional features */
35 
36 #include <gauche.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <gauche/class.h>
40 #include <gauche/extend.h>
41 
42 #include "gauche/fcntl.h"
43 
44 /* struct flock */
45 
46 static ScmObj flock_allocate(ScmClass *klass, ScmObj initargs);
47 
48 SCM_DEFINE_BUILTIN_CLASS(Scm_SysFlockClass,
49                          NULL, NULL, NULL,
50                          flock_allocate,
51                          NULL);
52 
flock_allocate(ScmClass * klass SCM_UNUSED,ScmObj initargs SCM_UNUSED)53 static ScmObj flock_allocate(ScmClass *klass SCM_UNUSED,
54                              ScmObj initargs SCM_UNUSED)
55 {
56     ScmSysFlock *f = SCM_NEW(ScmSysFlock);
57     SCM_SET_CLASS(f, SCM_CLASS_SYS_FLOCK);
58     memset(&f->lock, 0, sizeof(f->lock));
59     return SCM_OBJ(f);
60 }
61 
62 #define FLOCK_GET_N_SET(name, type, make, get)                            \
63   static ScmObj SCM_CPP_CAT3(flock_, name, _get)(ScmSysFlock* t)          \
64   { return make(t->lock.name); }                                          \
65   static void SCM_CPP_CAT3(flock_, name, _set)(ScmSysFlock* t, ScmObj v)  \
66   {                                                                       \
67       if (!SCM_INTEGERP(v)) Scm_Error("integer required, but got %S", v); \
68       t->lock.name = (type)get(v);                                        \
69   }
70 
71 FLOCK_GET_N_SET(l_type, short, Scm_MakeInteger, Scm_GetInteger)
72 FLOCK_GET_N_SET(l_whence, short, Scm_MakeInteger, Scm_GetInteger)
73 FLOCK_GET_N_SET(l_start, off_t, Scm_OffsetToInteger, Scm_IntegerToOffset)
74 FLOCK_GET_N_SET(l_len, off_t, Scm_OffsetToInteger, Scm_IntegerToOffset)
75 FLOCK_GET_N_SET(l_pid, pid_t, Scm_MakeInteger, Scm_GetInteger)
76 
77 static ScmClassStaticSlotSpec flock_slots[] = {
78     SCM_CLASS_SLOT_SPEC("type",   flock_l_type_get, flock_l_type_set),
79     SCM_CLASS_SLOT_SPEC("whence", flock_l_whence_get, flock_l_whence_set),
80     SCM_CLASS_SLOT_SPEC("start",  flock_l_start_get, flock_l_start_set),
81     SCM_CLASS_SLOT_SPEC("len",    flock_l_len_get, flock_l_len_set),
82     SCM_CLASS_SLOT_SPEC("pid",    flock_l_pid_get, flock_l_pid_set),
83     SCM_CLASS_SLOT_SPEC_END()
84 };
85 
Scm_MakeSysFlock(void)86 ScmObj Scm_MakeSysFlock(void)
87 {
88     return flock_allocate(SCM_CLASS_SYS_FLOCK, SCM_NIL);
89 }
90 
91 /*
92  * Fcntl bridge
93  */
94 #if defined(HAVE_FCNTL)
flag_name(int flag)95 static const char *flag_name(int flag)
96 {
97 #define FLAG_NAME(n) case n: return #n
98     switch (flag) {
99         FLAG_NAME(F_GETFD);
100         FLAG_NAME(F_SETFD);
101         FLAG_NAME(F_GETFL);
102         FLAG_NAME(F_SETFL);
103         FLAG_NAME(F_DUPFD);
104         FLAG_NAME(F_GETLK);
105         FLAG_NAME(F_SETLK);
106         FLAG_NAME(F_SETLKW);
107 #if defined(F_GETOWN)
108         FLAG_NAME(F_GETOWN);
109 #endif
110 #if defined(F_SETOWN)
111         FLAG_NAME(F_SETOWN);
112 #endif
113 #if defined(F_GETSIG)
114         FLAG_NAME(F_GETSIG);
115 #endif
116 #if defined(F_SETSIG)
117         FLAG_NAME(F_SETSIG);
118 #endif
119 #if defined(F_GETLEASE)
120         FLAG_NAME(F_GETLEASE);
121 #endif
122 #if defined(F_SETLEASE)
123         FLAG_NAME(F_SETLEASE);
124 #endif
125 #if defined(F_NOTIFY)
126         FLAG_NAME(F_NOTIFY);
127 #endif
128     }
129     return "(unknown flag)";
130 #undef FLAG_NAME
131 }
132 #endif /* HAVE_FCNTL */
133 
Scm_SysFcntl(ScmObj port_or_fd,int op,ScmObj arg)134 ScmObj Scm_SysFcntl(ScmObj port_or_fd, int op, ScmObj arg)
135 {
136 #if defined(HAVE_FCNTL)
137     int fd = Scm_GetPortFd(port_or_fd, TRUE), r;
138 
139     switch (op) {
140     case F_GETFD:; case F_GETFL:;
141 #if defined(F_GETOWN)           /* BSD and Linux specific */
142     case F_GETOWN:;
143 #endif /*F_GETOWN*/
144 #if defined(F_GETSIG)           /* Linux specific */
145     case F_GETSIG:;
146 #endif /*F_GETSIG */
147 #if defined(F_GETLEASE)         /* Linux specific */
148     case F_GETLEASE:;
149 #endif /*F_GETLEASE */
150         SCM_SYSCALL(r, fcntl(fd, op));
151         if (r == -1) { /*NB: F_GETOWN may return a negative value on success*/
152             Scm_SysError("fcntl(%s) failed", flag_name(op));
153         }
154         return Scm_MakeInteger(r);
155     case F_SETFD:; case F_SETFL:; case F_DUPFD:;
156 #if defined(F_SETOWN)           /* BSD and Linux specific */
157     case F_SETOWN:;
158 #endif /*F_SETOWN*/
159 #if defined(F_SETSIG)           /* Linux specific */
160     case F_SETSIG:;
161 #endif /*F_SETSIG */
162 #if defined(F_SETLEASE)         /* Linux specific */
163     case F_SETLEASE:;
164 #endif /*F_SETLEASE */
165 #if defined(F_NOTIFY)           /* Linux specific */
166     case F_NOTIFY:;
167 #endif /*F_NOTIFY */
168         if (!SCM_EXACTP(arg)) {
169             Scm_Error("exact integer required for fcntl(%s), but got %S",
170                       flag_name(op), arg);
171         }
172         SCM_SYSCALL(r, fcntl(fd, op, Scm_GetInteger(arg)));
173         if (r < 0) {
174             Scm_SysError("fcntl(%s) failed", flag_name(op));
175         }
176         return Scm_MakeInteger(r);
177     case F_GETLK:; case F_SETLK:; case F_SETLKW:;
178         if (!SCM_SYS_FLOCK_P(arg)) {
179             Scm_Error("flock object required for fcntl(%s), but got %S",
180                       flag_name(op), arg);
181         }
182         ScmSysFlock *fl = SCM_SYS_FLOCK(arg);
183         SCM_SYSCALL(r, fcntl(fd, op, &fl->lock));
184         if (op == F_SETLK) {
185             if (r >= 0) return SCM_TRUE;
186             if (errno == EAGAIN) return SCM_FALSE;
187         }
188         if (r < 0) Scm_SysError("fcntl(%s) failed", flag_name(op));
189         return SCM_TRUE;
190     default:
191         Scm_Error("unknown operation code (%d) for fcntl", op);
192         return SCM_UNDEFINED;   /* dummy */
193     }
194 #else  /*!HAVE_FCNTL*/
195     (void)port_or_fd; /* suppress unused var warning */
196     (void)op;         /* suppress unused var warning */
197     (void)arg;        /* suppress unused var warning */
198     Scm_Error("fcntl not supported on MinGW port");
199     return SCM_UNDEFINED; /*dummy*/
200 #endif /*!HAVE_FCNTL*/
201 }
202 
203 /*
204  * Initialization
205  */
206 
Scm_Init_fcntl(void)207 void Scm_Init_fcntl(void)
208 {
209     ScmModule *mod = SCM_FIND_MODULE("gauche.fcntl", SCM_FIND_MODULE_CREATE);
210     Scm_InitStaticClass(&Scm_SysFlockClass, "<sys-flock>",
211                         mod, flock_slots, 0);
212 }
213