Cygwin: add newgrp release notes
[newlib-cygwin.git] / winsup / cygwin / resource.cc
blob5ec436c2cfa43ca077c74ea56af551734cd68049
1 /* resource.cc: getrusage () and friends.
3 Written by Steve Chamberlain (sac@cygnus.com), Doug Evans (dje@cygnus.com),
4 Geoffrey Noer (noer@cygnus.com) of Cygnus Support.
5 Rewritten by Sergey S. Okhapkin (sos@prospect.com.ru)
7 This file is part of Cygwin.
9 This software is a copyrighted work licensed under the terms of the
10 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
11 details. */
13 #include "winsup.h"
14 #include <unistd.h>
15 #include <sys/param.h>
16 #include "pinfo.h"
17 #include "cygtls.h"
18 #include "path.h"
19 #include "fhandler.h"
20 #include "pinfo.h"
21 #include "dtable.h"
22 #include "cygheap.h"
23 #include "shared_info.h"
24 #include "ntdll.h"
26 /* add timeval values */
27 static void
28 add_timeval (struct timeval *tv1, struct timeval *tv2)
30 tv1->tv_sec += tv2->tv_sec;
31 tv1->tv_usec += tv2->tv_usec;
32 if (tv1->tv_usec >= USPERSEC)
34 tv1->tv_usec -= USPERSEC;
35 tv1->tv_sec++;
39 /* add rusage values of r2 to r1 */
40 void
41 add_rusage (struct rusage *r1, struct rusage *r2)
43 add_timeval (&r1->ru_utime, &r2->ru_utime);
44 add_timeval (&r1->ru_stime, &r2->ru_stime);
45 r1->ru_maxrss += r2->ru_maxrss;
46 r1->ru_ixrss += r2->ru_ixrss;
47 r1->ru_idrss += r2->ru_idrss;
48 r1->ru_isrss += r2->ru_isrss;
49 r1->ru_minflt += r2->ru_minflt;
50 r1->ru_majflt += r2->ru_majflt;
51 r1->ru_nswap += r2->ru_nswap;
52 r1->ru_inblock += r2->ru_inblock;
53 r1->ru_oublock += r2->ru_oublock;
54 r1->ru_msgsnd += r2->ru_msgsnd;
55 r1->ru_msgrcv += r2->ru_msgrcv;
56 r1->ru_nsignals += r2->ru_nsignals;
57 r1->ru_nvcsw += r2->ru_nvcsw;
58 r1->ru_nivcsw += r2->ru_nivcsw;
61 /* FIXME: what about other fields? */
62 void
63 fill_rusage (struct rusage *r, HANDLE h)
65 KERNEL_USER_TIMES kut;
67 struct timeval tv;
69 memset (&kut, 0, sizeof kut);
70 memset (r, 0, sizeof *r);
71 NtQueryInformationProcess (h, ProcessTimes, &kut, sizeof kut, NULL);
72 totimeval (&tv, &kut.KernelTime, 0, 0);
73 add_timeval (&r->ru_stime, &tv);
74 totimeval (&tv, &kut.UserTime, 0, 0);
75 add_timeval (&r->ru_utime, &tv);
77 VM_COUNTERS vmc;
78 NTSTATUS status = NtQueryInformationProcess (h, ProcessVmCounters, &vmc,
79 sizeof vmc, NULL);
80 if (NT_SUCCESS (status))
82 r->ru_maxrss += (long) (vmc.WorkingSetSize / 1024);
83 r->ru_majflt += vmc.PageFaultCount;
87 extern "C" int
88 getrusage (int intwho, struct rusage *rusage_in)
90 int res = 0;
91 struct rusage r;
93 if (intwho == RUSAGE_SELF)
95 memset (&r, 0, sizeof (r));
96 fill_rusage (&r, GetCurrentProcess ());
97 *rusage_in = r;
99 else if (intwho == RUSAGE_CHILDREN)
100 *rusage_in = myself->rusage_children;
101 else
103 set_errno (EINVAL);
104 res = -1;
107 syscall_printf ("%R = getrusage(%d, %p)", res, intwho, rusage_in);
108 return res;
111 /* Default stacksize in case RLIMIT_STACK is RLIM_INFINITY is 2 Megs with
112 system-dependent number of guard pages. The pthread stacksize does not
113 include the guardpage size, so we have to subtract the default guardpage
114 size. Additionally the Windows stack handling disallows to commit the
115 last page, so we subtract it, too. */
116 #define DEFAULT_STACKSIZE (2 * 1024 * 1024)
117 #define DEFAULT_STACKGUARD (wincap.def_guard_page_size() + wincap.page_size ())
119 muto NO_COPY rlimit_stack_guard;
120 static struct rlimit rlimit_stack = { 0, RLIM_INFINITY };
122 static void
123 __set_rlimit_stack (const struct rlimit *rlp)
125 rlimit_stack_guard.init ("rlimit_stack_guard")->acquire ();
126 rlimit_stack = *rlp;
127 rlimit_stack_guard.release ();
130 static void
131 __get_rlimit_stack (struct rlimit *rlp)
133 rlimit_stack_guard.init ("rlimit_stack_guard")->acquire ();
134 if (!rlimit_stack.rlim_cur)
136 /* Fetch the default stacksize from the executable header... */
137 PIMAGE_DOS_HEADER dosheader;
138 PIMAGE_NT_HEADERS ntheader;
140 dosheader = (PIMAGE_DOS_HEADER) GetModuleHandle (NULL);
141 ntheader = (PIMAGE_NT_HEADERS) ((PBYTE) dosheader + dosheader->e_lfanew);
142 rlimit_stack.rlim_cur = ntheader->OptionalHeader.SizeOfStackReserve;
143 /* ...and subtract the guardpages. */
144 rlimit_stack.rlim_cur -= DEFAULT_STACKGUARD;
146 *rlp = rlimit_stack;
147 rlimit_stack_guard.release ();
150 /* Interface to stack limit called from pthread_create and
151 pthread_attr_getstacksize. */
152 size_t
153 get_rlimit_stack (void)
155 struct rlimit rl;
157 __get_rlimit_stack (&rl);
158 /* RLIM_INFINITY doesn't make much sense. As in glibc, use an
159 "architecture-specific default". */
160 if (rl.rlim_cur == RLIM_INFINITY)
161 rl.rlim_cur = DEFAULT_STACKSIZE - DEFAULT_STACKGUARD;
162 /* Always return at least minimum stacksize. */
163 else if (rl.rlim_cur < PTHREAD_STACK_MIN)
164 rl.rlim_cur = PTHREAD_STACK_MIN;
165 return (size_t) rl.rlim_cur;
168 static LONG job_serial_number __attribute__((section (".cygwin_dll_common"), shared));
170 static PWCHAR
171 job_shared_name (PWCHAR buf, LONG num)
173 __small_swprintf (buf, L"rlimit.%d", num);
174 return buf;
177 static void
178 __get_rlimit_as (struct rlimit *rlp)
180 UNICODE_STRING uname;
181 WCHAR jobname[32];
182 OBJECT_ATTRIBUTES attr;
183 HANDLE job = NULL;
184 NTSTATUS status;
185 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobinfo;
187 if (cygheap->rlim_as_id)
189 RtlInitUnicodeString (&uname,
190 job_shared_name (jobname,
191 cygheap->rlim_as_id));
192 InitializeObjectAttributes (&attr, &uname, 0,
193 get_session_parent_dir (), NULL);
194 /* May fail, just check NULL job in that case. */
195 NtOpenJobObject (&job, JOB_OBJECT_QUERY, &attr);
197 status = NtQueryInformationJobObject (job,
198 JobObjectExtendedLimitInformation,
199 &jobinfo, sizeof jobinfo, NULL);
200 if (NT_SUCCESS (status)
201 && (jobinfo.BasicLimitInformation.LimitFlags
202 & JOB_OBJECT_LIMIT_PROCESS_MEMORY))
203 rlp->rlim_cur = rlp->rlim_max = jobinfo.ProcessMemoryLimit;
204 if (job)
205 NtClose (job);
208 static int
209 __set_rlimit_as (unsigned long new_as_limit)
211 LONG new_as_id = 0;
212 UNICODE_STRING uname;
213 WCHAR jobname[32];
214 OBJECT_ATTRIBUTES attr;
215 NTSTATUS status = STATUS_SUCCESS;
216 HANDLE job = NULL;
217 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobinfo = { 0 };
219 /* If we already have a limit, we must not change it because that
220 would potentially influence already running child processes.
221 Just try to create another, nested job. */
222 while (new_as_id == 0)
223 new_as_id = InterlockedIncrement (&job_serial_number);
224 RtlInitUnicodeString (&uname,
225 job_shared_name (jobname, new_as_id));
226 InitializeObjectAttributes (&attr, &uname, 0,
227 get_session_parent_dir (), NULL);
228 status = NtCreateJobObject (&job, JOB_OBJECT_ALL_ACCESS, &attr);
229 if (!NT_SUCCESS (status))
231 __seterrno_from_nt_status (status);
232 return -1;
234 jobinfo.BasicLimitInformation.LimitFlags
235 = JOB_OBJECT_LIMIT_PROCESS_MEMORY;
236 /* Per Linux man page, round down to system pagesize. */
237 jobinfo.ProcessMemoryLimit
238 = rounddown (new_as_limit, wincap.allocation_granularity ());
239 status = NtSetInformationJobObject (job,
240 JobObjectExtendedLimitInformation,
241 &jobinfo, sizeof jobinfo);
242 /* If creating the job and setting up the job limits succeeded,
243 try to add the process to the job. This must be the last step,
244 otherwise we couldn't remove the job if anything failed. */
245 if (NT_SUCCESS (status))
246 status = NtAssignProcessToJobObject (job, NtCurrentProcess ());
247 NtClose (job);
248 if (!NT_SUCCESS (status))
250 __seterrno_from_nt_status (status);
251 return -1;
253 cygheap->rlim_as_id = new_as_id;
254 return 0;
257 extern "C" int
258 getrlimit (int resource, struct rlimit *rlp)
260 __try
262 rlp->rlim_cur = RLIM_INFINITY;
263 rlp->rlim_max = RLIM_INFINITY;
265 switch (resource)
267 case RLIMIT_CPU:
268 case RLIMIT_FSIZE:
269 case RLIMIT_DATA:
270 break;
271 case RLIMIT_AS:
272 __get_rlimit_as (rlp);
273 break;
274 case RLIMIT_STACK:
275 __get_rlimit_stack (rlp);
276 break;
277 case RLIMIT_NOFILE:
278 rlp->rlim_cur = rlp->rlim_max = OPEN_MAX;
279 break;
280 case RLIMIT_CORE:
281 rlp->rlim_cur = cygheap->rlim_core;
282 break;
283 default:
284 set_errno (EINVAL);
285 __leave;
287 return 0;
289 __except (EFAULT) {}
290 __endtry
291 return -1;
294 extern "C" int
295 setrlimit (int resource, const struct rlimit *rlp)
297 struct rlimit oldlimits;
299 /* Check if the request is to actually change the resource settings.
300 If it does not result in a change, take no action and do not fail. */
301 if (getrlimit (resource, &oldlimits) < 0)
302 return -1;
304 __try
306 if (oldlimits.rlim_cur == rlp->rlim_cur &&
307 oldlimits.rlim_max == rlp->rlim_max)
308 /* No change in resource requirements, succeed immediately */
309 return 0;
311 if (rlp->rlim_cur > rlp->rlim_max)
313 set_errno (EINVAL);
314 __leave;
317 if (rlp->rlim_cur > oldlimits.rlim_max)
319 set_errno (EPERM);
320 __leave;
323 switch (resource)
325 case RLIMIT_AS:
326 if (rlp->rlim_cur != RLIM_INFINITY)
327 return __set_rlimit_as (rlp->rlim_cur);
328 break;
329 case RLIMIT_CORE:
330 cygheap->rlim_core = rlp->rlim_cur;
331 break;
332 case RLIMIT_NOFILE:
333 if (rlp->rlim_cur != RLIM_INFINITY)
334 return setdtablesize (rlp->rlim_cur);
335 break;
336 case RLIMIT_STACK:
337 __set_rlimit_stack (rlp);
338 break;
339 default:
340 set_errno (EINVAL);
341 __leave;
343 return 0;
345 __except (EFAULT)
346 __endtry
347 return -1;