Merge from vendor branch OPENSSH:
[dragonfly.git] / sys / checkpt / checkpt.c
1 /*-
2  * Copyright (c) 2003 Kip Macy
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $DragonFly: src/sys/checkpt/Attic/checkpt.c,v 1.5 2004/06/03 10:00:06 eirikn Exp $
27  */
28
29 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/proc.h>
32 #include <sys/module.h>
33 #include <sys/sysent.h>
34 #include <sys/kernel.h>
35 #include <sys/systm.h>
36
37 #include <sys/file.h>
38 /* only on dragonfly */
39 #include <sys/file2.h>
40 #include <sys/fcntl.h>
41 #include <sys/signal.h>
42 #include <vm/vm_param.h>
43 #include <vm/vm.h>
44 #include <sys/imgact_elf.h>
45 #include <sys/procfs.h>
46
47 #include <sys/lock.h>
48 #include <vm/pmap.h>
49 #include <vm/vm_map.h>
50
51 #include <sys/mman.h>
52 #include <sys/sysproto.h>
53 #include <sys/resource.h>
54 #include <sys/resourcevar.h>
55 #include <sys/malloc.h>
56 #include <sys/stat.h>
57 #include <sys/uio.h>
58 #include <sys/namei.h>
59 #include <sys/vnode.h>
60 #include <machine/limits.h>
61 #include <i386/include/frame.h>
62 #include <sys/signalvar.h>
63 #include <sys/syslog.h>
64 #include <sys/sysctl.h>
65 #include <i386/include/sigframe.h>
66 #include <sys/exec.h>
67 #include <sys/unistd.h>
68 #include <sys/time.h>
69 #include "checkpt.h"
70 #include <sys/mount.h>
71 #include <sys/ckpt.h>
72
73
74 static int elf_loadphdrs(struct file *fp,  Elf_Phdr *phdr, int numsegs);
75 static int elf_getnotes(struct proc *p, struct file *fp, size_t notesz);
76 static int elf_demarshalnotes(void *src, prpsinfo_t *psinfo,
77                  prstatus_t *status, prfpregset_t *fpregset, int nthreads); 
78 static int elf_loadnotes(struct proc *, prpsinfo_t *, prstatus_t *, 
79                  prfpregset_t *);
80 static int elf_getsigs(struct proc *p, struct file *fp); 
81 static int elf_getfiles(struct proc *p, struct file *fp);
82 static int elf_gettextvp(struct proc *p, struct file *fp);
83
84 static int ckptgroup = 0;       /* wheel only, -1 for any group */
85 SYSCTL_INT(_kern, OID_AUTO, ckptgroup, CTLFLAG_RW, &ckptgroup, 0, "");
86
87 /* ref count to see how many processes that are being checkpointed */
88 static int chptinuse = 0;
89
90 static __inline
91 int
92 read_check(struct file *fp, void *buf, size_t nbyte)
93 {
94         size_t nread;
95         int error;
96
97         PRINTF(("reading %d bytes\n", nbyte));
98         error = fp_read(fp, buf, nbyte, &nread);
99         if (error) {
100                 PRINTF(("read failed - %d", error));
101         } else if (nread != nbyte) {
102                 PRINTF(("wanted to read %d - read %d\n", nbyte, nread));
103                 error = EINVAL;
104         }
105         return error;
106 }
107
108 static int
109 elf_gethdr(struct file *fp, Elf_Ehdr *ehdr) 
110 {
111         size_t nbyte = sizeof(Elf_Ehdr);
112         int error;
113
114         if ((error = read_check(fp, ehdr, nbyte)) != 0)
115                 goto done;
116         if (!(ehdr->e_ehsize == sizeof(Elf_Ehdr))) {
117                 PRINTF(("wrong elf header size: %d\n"
118                        "expected size        : %d\n", 
119                        ehdr->e_ehsize, sizeof(Elf_Ehdr)));
120                 return EINVAL;
121         }
122         if (!(ehdr->e_phentsize == sizeof(Elf_Phdr))) {
123                 PRINTF(("wrong program header size: %d\n"
124                        "expected size            : %d\n",  
125                        ehdr->e_phentsize, sizeof(Elf_Phdr)));
126                 return EINVAL;
127         }
128
129         if (!(ehdr->e_ident[EI_MAG0] == ELFMAG0 &&
130               ehdr->e_ident[EI_MAG1] == ELFMAG1 &&
131               ehdr->e_ident[EI_MAG2] == ELFMAG2 &&
132               ehdr->e_ident[EI_MAG3] == ELFMAG3 &&
133               ehdr->e_ident[EI_CLASS] == ELF_CLASS &&
134               ehdr->e_ident[EI_DATA] == ELF_DATA &&
135               ehdr->e_ident[EI_VERSION] == EV_CURRENT &&
136               ehdr->e_ident[EI_OSABI] == ELFOSABI_FREEBSD &&
137               ehdr->e_ident[EI_ABIVERSION] == 0)) {
138                 PRINTF(("bad elf header\n there are %d segments\n",
139                        ehdr->e_phnum));
140                 return EINVAL;
141
142         }
143         PRINTF(("Elf header size:           %d\n", ehdr->e_ehsize));
144         PRINTF(("Program header size:       %d\n", ehdr->e_phentsize));
145         PRINTF(("Number of Program headers: %d\n", ehdr->e_phnum));
146  done:
147         return error;
148
149
150 static int
151 elf_getphdrs(struct file *fp, Elf_Phdr *phdr, size_t nbyte) 
152 {
153         int i;
154         int error;
155         int nheaders = nbyte/sizeof(Elf_Phdr); 
156
157         PRINTF(("reading phdrs section\n"));
158         if ((error = read_check(fp, phdr, nbyte)) != 0)
159                 goto done;
160         printf("headers section:\n");
161         for (i = 0; i < nheaders; i++) {
162                 printf("entry type:   %d\n", phdr[i].p_type);
163                 printf("file offset:  %d\n", phdr[i].p_offset);
164                 printf("virt address: %p\n", (uint32_t *)phdr[i].p_vaddr);
165                 printf("file size:    %d\n", phdr[i].p_filesz);
166                 printf("memory size:  %d\n", phdr[i].p_memsz);
167                 printf("\n");
168         }
169  done:
170         return error;
171 }
172
173
174 static int
175 elf_getnotes(struct proc *p, struct file *fp, size_t notesz) 
176 {
177         int error;
178         int nthreads;
179         char *note;
180         prpsinfo_t *psinfo;
181         prstatus_t *status;
182         prfpregset_t *fpregset;
183
184         nthreads = (notesz - sizeof(prpsinfo_t))/(sizeof(prstatus_t) + 
185                                                   sizeof(prfpregset_t));
186         PRINTF(("reading notes header nthreads=%d\n", nthreads));
187         if (nthreads <= 0 || nthreads > CKPT_MAXTHREADS)
188                 return EINVAL;
189
190         psinfo  = malloc(sizeof(prpsinfo_t), M_TEMP, M_ZERO | M_WAITOK);
191         status  = malloc(nthreads*sizeof(prstatus_t), M_TEMP, M_WAITOK);
192         fpregset  = malloc(nthreads*sizeof(prfpregset_t), M_TEMP, M_WAITOK);
193         note = malloc(notesz, M_TEMP, M_WAITOK);
194
195         
196         PRINTF(("reading notes section\n"));
197         if ((error = read_check(fp, note, notesz)) != 0)
198                 goto done;
199         error = elf_demarshalnotes(note, psinfo, status, fpregset, nthreads);
200         if (error)
201                 goto done;
202         /* fetch register state from notes */
203         error = elf_loadnotes(p, psinfo, status, fpregset);
204  done:
205         if (psinfo)
206                 free(psinfo, M_TEMP);
207         if (status)
208                 free(status, M_TEMP);
209         if (fpregset)
210                 free(fpregset, M_TEMP);
211         if (note)
212                 free(note, M_TEMP);
213         return error;
214 }
215
216 static int
217 ckpt_thaw_proc(struct proc *p, struct file *fp)
218 {
219
220         Elf_Phdr *phdr = NULL;
221         Elf_Ehdr *ehdr = NULL;
222         int error;
223         size_t nbyte;
224
225         TRACE_ENTER;
226         
227         ehdr = malloc(sizeof(Elf_Ehdr), M_TEMP, M_ZERO | M_WAITOK);
228
229         if ((error = elf_gethdr(fp, ehdr)) != 0)
230                 goto done;
231         nbyte = sizeof(Elf_Phdr) * ehdr->e_phnum; 
232         phdr = malloc(nbyte, M_TEMP, M_WAITOK); 
233
234         /* fetch description of program writable mappings */
235         if ((error = elf_getphdrs(fp, phdr, nbyte)) != 0)
236                 goto done;
237
238         /* fetch notes section containing register state */
239         if ((error = elf_getnotes(p, fp, phdr->p_filesz)) != 0)
240                 goto done;
241
242         /* fetch program text vnodes */
243         if ((error = elf_gettextvp(p, fp)) != 0)
244                 goto done;
245
246         /* fetch signal disposition */
247         if ((error = elf_getsigs(p, fp)) != 0)
248                 goto done;
249
250         /* fetch open files */
251         if ((error = elf_getfiles(p, fp)) != 0)
252                 goto done;
253
254         /* handle mappings last in case we are reading from a socket */
255         error = elf_loadphdrs(fp, phdr, ehdr->e_phnum);
256 done:
257         if (ehdr)
258                 free(ehdr, M_TEMP);
259         if (phdr)
260                 free(phdr, M_TEMP);
261         TRACE_EXIT;
262         return error;
263 }
264
265 static int
266 elf_loadnotes(struct proc *p, prpsinfo_t *psinfo, prstatus_t *status, 
267            prfpregset_t *fpregset) 
268 {
269         int error;
270
271         /* validate status and psinfo */
272         TRACE_ENTER;
273         if (status->pr_version != PRSTATUS_VERSION ||
274             status->pr_statussz != sizeof(prstatus_t) ||
275             status->pr_gregsetsz != sizeof(gregset_t) ||
276             status->pr_fpregsetsz != sizeof(fpregset_t) ||
277             psinfo->pr_version != PRPSINFO_VERSION ||
278             psinfo->pr_psinfosz != sizeof(prpsinfo_t)) {
279                 PRINTF(("status check failed\n"));
280                 error = EINVAL;
281                 goto done;
282         }
283         if ((error = set_regs(p, &status->pr_reg)) != 0)
284                 goto done;
285         error = set_fpregs(p, fpregset);
286         /* strncpy(psinfo->pr_psargs, p->p_comm, PRARGSZ); */
287  done:  
288         TRACE_EXIT;
289         return error;
290 }
291
292 static int 
293 elf_getnote(void *src, size_t *off, const char *name, unsigned int type,
294             void **desc, size_t descsz) 
295 {
296         Elf_Note note;
297         int error;
298
299         TRACE_ENTER;
300         if (src == NULL) {
301                 error = EFAULT;
302                 goto done;
303         }
304         bcopy((char *)src + *off, &note, sizeof note);
305         
306         PRINTF(("at offset: %d expected note of type: %d - got: %d\n",
307                *off, type, note.n_type));
308         *off += sizeof note;
309         if (type != note.n_type) {
310                 TRACE_ERR;
311                 error = EINVAL;
312                 goto done;
313         }
314         if (strncmp(name, (char *) src + *off, note.n_namesz) != 0) {
315                 error = EINVAL;
316                 goto done;
317         }
318         *off += roundup2(note.n_namesz, sizeof(Elf_Size));
319         if (note.n_descsz != descsz) {
320                 TRACE_ERR;
321                 error = EINVAL;
322                 goto done;
323         }
324         if (desc)
325                 bcopy((char *)src + *off, *desc, note.n_descsz);
326         *off += roundup2(note.n_descsz, sizeof(Elf_Size));
327         error = 0;
328  done:
329         TRACE_EXIT;
330         return error;
331 }
332
333 static int
334 elf_demarshalnotes(void *src, prpsinfo_t *psinfo, prstatus_t *status, 
335                    prfpregset_t *fpregset, int nthreads) 
336 {
337         int i;
338         int error;
339         int off = 0;
340
341         TRACE_ENTER;
342         error = elf_getnote(src, &off, "FreeBSD", NT_PRSTATUS, 
343                            (void **)&status, sizeof(prstatus_t));
344         if (error)
345                 goto done;
346         error = elf_getnote(src, &off, "FreeBSD", NT_FPREGSET, 
347                            (void **)&fpregset, sizeof(prfpregset_t));
348         if (error)
349                 goto done;
350         error = elf_getnote(src, &off, "FreeBSD", NT_PRPSINFO, 
351                            (void **)&psinfo, sizeof(prpsinfo_t));
352         if (error)
353                 goto done;
354
355         /*
356          * The remaining portion needs to be an integer multiple
357          * of prstatus_t and prfpregset_t
358          */
359         for (i = 0 ; i < nthreads - 1; i++) {
360                 status++; fpregset++;
361                 error = elf_getnote(src, &off, "FreeBSD", NT_PRSTATUS, 
362                                    (void **)&status, sizeof (prstatus_t));
363                 if (error)
364                         goto done;
365                 error = elf_getnote(src, &off, "FreeBSD", NT_FPREGSET, 
366                                    (void **)&fpregset, sizeof(prfpregset_t));
367                 if (error)
368                         goto done;
369         }
370         
371  done:
372         TRACE_EXIT;
373         return error;
374 }
375
376
377 static int
378 mmap_phdr(struct file *fp, Elf_Phdr *phdr) 
379 {
380         int error;
381         size_t len;
382         int prot;
383         void *addr;
384         int flags;
385         off_t pos;
386
387         TRACE_ENTER;
388         pos = phdr->p_offset;
389         len = phdr->p_filesz;
390         addr = (void *)phdr->p_vaddr;
391         flags = MAP_FIXED | MAP_NOSYNC | MAP_PRIVATE;
392         prot = 0;
393         if (phdr->p_flags & PF_R)
394                 prot |= PROT_READ;
395         if (phdr->p_flags & PF_W)
396                 prot |= PROT_WRITE;
397         if (phdr->p_flags & PF_X)
398                 prot |= PROT_EXEC;      
399         if ((error = fp_mmap(addr, len, prot, flags, fp, pos, &addr)) != 0) {
400                 PRINTF(("mmap failed: %d\n", error);       );
401         }
402         PRINTF(("map @%08x-%08x fileoff %08x-%08x\n", (int)addr, (int)((char *)addr + len), (int)pos, (int)(pos + len)));
403         TRACE_EXIT;
404         return error;
405 }
406
407
408 static int
409 elf_loadphdrs(struct file *fp, Elf_Phdr *phdr, int numsegs) 
410 {
411         int i;
412         int error = 0;
413
414         TRACE_ENTER;
415         for (i = 1; i < numsegs; i++)  {
416                 if ((error = mmap_phdr(fp, &phdr[i])) != 0)
417                         break;
418         }
419         TRACE_EXIT;
420         return error;
421 }
422
423 static int
424 elf_getsigs(struct proc *p, struct file *fp) 
425 {
426         int error;
427         struct ckpt_siginfo *csi;
428         struct sigacts *tmpsigacts;
429
430         TRACE_ENTER;
431         csi = malloc(sizeof(struct ckpt_siginfo), M_TEMP, M_ZERO | M_WAITOK);
432         if ((error = read_check(fp, csi, sizeof(struct ckpt_siginfo))) != 0)
433                 goto done;
434
435         if (csi->csi_ckptpisz != sizeof(struct ckpt_siginfo)) {
436                 TRACE_ERR;
437                 error = EINVAL;
438                 goto done;
439         }
440         tmpsigacts = p->p_procsig->ps_sigacts;
441         bcopy(&csi->csi_procsig, p->p_procsig, sizeof(struct procsig));
442         p->p_procsig->ps_sigacts = tmpsigacts;
443         bcopy(&csi->csi_sigacts, p->p_procsig->ps_sigacts, sizeof(struct sigacts));
444         bcopy(&csi->csi_itimerval, &p->p_realtimer, sizeof(struct itimerval));
445         p->p_sigparent = csi->csi_sigparent;
446  done:
447         if (csi)
448                 free(csi, M_TEMP);
449         TRACE_EXIT;
450         return error;
451 }
452
453
454 static int
455 ckpt_fhtovp(fhandle_t *fh, struct vnode **vpp) 
456 {
457         struct mount *mp;
458         int error;
459
460         TRACE_ENTER;
461         mp = vfs_getvfs(&fh->fh_fsid);
462
463         if (!mp) {
464                 TRACE_ERR;
465                 PRINTF(("failed to get mount - ESTALE\n"));
466                 TRACE_EXIT;
467                 return ESTALE;
468         }
469         error = VFS_FHTOVP(mp, &fh->fh_fid, vpp);
470         if (error) {
471                 PRINTF(("failed with: %d\n", error));
472                 TRACE_ERR;
473                 TRACE_EXIT;
474                 return error;
475         }
476         TRACE_EXIT;
477         return 0;
478 }
479
480 static int
481 mmap_vp(struct vn_hdr *vnh) 
482 {
483         struct vnode **vpp, *vp;
484         Elf_Phdr *phdr;
485         struct file *fp;
486         int error;
487         TRACE_ENTER;
488         vpp = &vp;
489
490         phdr = &vnh->vnh_phdr;
491
492         if ((error = ckpt_fhtovp(&vnh->vnh_fh, vpp)) != 0)
493                 return error;
494         /*
495          * XXX O_RDONLY -> or O_RDWR if file is PROT_WRITE, MAP_SHARED
496          */
497         if ((error = fp_vpopen(*vpp, O_RDONLY, &fp)) != 0)
498                 return error;
499         error = mmap_phdr(fp, phdr);
500         fp_close(fp);
501         TRACE_EXIT;
502         return error;
503 }
504
505
506 static int
507 elf_gettextvp(struct proc *p, struct file *fp)
508 {
509         int i;
510         int error;
511         int vpcount;
512         struct ckpt_vminfo vminfo;
513         struct vn_hdr *vnh = NULL;
514
515         TRACE_ENTER;
516         if ((error = read_check(fp, &vminfo, sizeof(vminfo))) != 0)
517                 goto done;
518         if (vminfo.cvm_dsize < 0 || 
519             vminfo.cvm_dsize > p->p_rlimit[RLIMIT_DATA].rlim_cur ||
520             vminfo.cvm_tsize < 0 ||
521             (u_quad_t)vminfo.cvm_tsize > maxtsiz ||
522             vminfo.cvm_daddr >= (caddr_t)VM_MAXUSER_ADDRESS ||
523             vminfo.cvm_taddr >= (caddr_t)VM_MAXUSER_ADDRESS
524         ) {
525             error = ERANGE;
526             goto done;
527         }
528         p->p_vmspace->vm_daddr = vminfo.cvm_daddr;
529         p->p_vmspace->vm_dsize = vminfo.cvm_dsize;
530         p->p_vmspace->vm_taddr = vminfo.cvm_taddr;
531         p->p_vmspace->vm_tsize = vminfo.cvm_tsize;
532         if ((error = read_check(fp, &vpcount, sizeof(int))) != 0)
533                 goto done;
534         vnh = malloc(sizeof(struct vn_hdr) * vpcount, M_TEMP, M_WAITOK);
535         if ((error = read_check(fp, vnh, sizeof(struct vn_hdr)*vpcount)) != 0)
536                 goto done;
537         for (i = 0; i < vpcount; i++) {
538                 if ((error = mmap_vp(&vnh[i])) != 0)
539                         goto done;
540         }
541         
542  done:
543         if (vnh)
544                 free(vnh, M_TEMP);
545         TRACE_EXIT;
546         return error;
547 }
548
549
550
551 /* place holder */
552 static int
553 elf_getfiles(struct proc *p, struct file *fp)
554 {
555         int error;
556         int i;
557         int filecount;
558         struct ckpt_filehdr filehdr;
559         struct ckpt_fileinfo *cfi_base = NULL;
560         struct vnode *vp;
561         struct file *tempfp;
562
563         TRACE_ENTER;
564         if ((error = read_check(fp, &filehdr, sizeof(filehdr))) != 0)
565                 goto done;
566         filecount = filehdr.cfh_nfiles;
567         cfi_base = malloc(filecount*sizeof(struct ckpt_fileinfo), M_TEMP, M_WAITOK);
568         error = read_check(fp, cfi_base, filecount*sizeof(struct ckpt_fileinfo));
569         if (error)
570                 goto done;
571
572         for (i = 0; i < filecount; i++) {
573                 struct ckpt_fileinfo *cfi= &cfi_base[i];
574                 /*
575                  * Ignore placeholder entries where cfi_index is less then
576                  * zero.  This will occur if the elf core dump code thinks
577                  * it can save a vnode but winds up not being able to.
578                  */
579                 if (cfi->cfi_index < 0)
580                         continue;
581                 if (cfi->cfi_index >=  p->p_fd->fd_nfiles) {
582                         PRINTF(("can't currently restore fd: %d\n",
583                                cfi->cfi_index));
584                         goto done;
585                 }
586                 if ((error = ckpt_fhtovp(&cfi->cfi_fh, &vp)) != 0)
587                         break;
588                 if ((error = fp_vpopen(vp, OFLAGS(cfi->cfi_flags), &tempfp)) != 0)
589                         break;
590                 tempfp->f_offset = cfi->cfi_offset;
591                 /*  XXX bail for now if we the index is 
592                  *  larger than the current file table 
593                  */
594
595                 PRINTF(("restoring %d\n", cfi->cfi_index));
596                 p->p_fd->fd_ofiles[cfi->cfi_index] = tempfp;            
597                 cfi++;
598         }
599
600  done:
601         if (cfi_base)
602                 free(cfi_base, M_TEMP);
603         TRACE_EXIT;
604         return error;
605 }
606
607 static int
608 ckpt_freeze_proc (struct proc *p, struct file *fp)
609 {
610         rlim_t limit;
611         int error;
612
613         PRINTF(("calling generic_elf_coredump\n"));
614         limit = p->p_rlimit[RLIMIT_CORE].rlim_cur;
615         if (limit) {
616                 error = generic_elf_coredump(p, fp, limit);
617         } else {
618                 error = ERANGE;
619         }
620         return error;
621 }
622
623 #if 0
624 /* THIS CAN'T WORK */
625 static int
626 ckpt_freeze_pid(int pid, struct file *fp) 
627 {
628         struct proc *p;
629
630         if ((p = pfind(pid)) == NULL)
631                 return ESRCH;
632         return ckpt_freeze_proc(p, fp);
633 }
634 #endif
635
636 static int 
637 ckpt_proc(void *uap /* struct ckpt_args */)
638 {
639         int error = 0;
640         int *res;
641         struct proc *p = curthread->td_proc;
642         struct ckpt_args *ca = (struct ckpt_args *)uap; 
643         struct file *fp;
644
645         res = &((struct nosys_args *)uap)->sysmsg_result;
646
647         /*
648          * Only certain groups (to reduce our security exposure).  -1
649          * allows any group.
650          */
651         if (ckptgroup >= 0 && groupmember(ckptgroup, p->p_ucred) == 0) {
652                 error = EPERM;
653                 goto done;
654         }
655         switch (ca->args.gen.type) {
656         case CKPT_FREEZE:
657                 if ((fp = holdfp(p->p_fd, ca->args.gen.fd, FWRITE)) == NULL)
658                         return EBADF;
659                 error = ckpt_freeze_proc(p, fp);
660                 fdrop(fp, curthread);
661                 break;
662         case CKPT_THAW:
663                 if ((fp = holdfp(p->p_fd, ca->args.gen.fd, FREAD)) == NULL)
664                         return EBADF;
665                 *res = ca->args.cta.retval;
666                 error = ckpt_thaw_proc(p, fp);
667                 fdrop(fp, curthread);
668                 break;
669         case CKPT_FREEZEPID:
670                 error = ENOSYS;
671                 break; 
672 #if 0
673                 /* doesn't work */
674                 if ((fp = holdfp(p->p_fd, ca->args.gen.fd, FWRITE)) == NULL)
675                         return EBADF;
676                 error = ckpt_freeze_pid(ca->args.cfpa.pid, fp);
677                 fdrop(fp, curthread);
678                 break;
679 #endif
680         case CKPT_THAWBIN:
681                 error = ENOSYS;
682                 break;
683 #if 0
684                 /* not supported */
685                 if ((fp = holdfp(p->p_fd, ca->args.gen.fd, FREAD)) == NULL)
686                         return EBADF;
687                 error = ckpt_thaw_bin(p, fp,
688                                 ca->args.ctba.bin, ca->args.ctba.binlen);
689                 fdrop(fp, curthread);
690                 break;
691 #endif
692         default:
693                 error = ENOSYS;
694                 break;
695         }
696 done:
697         PRINTF(("error of ckpt_proc is %d, retval is %d\n", error, *res));
698         return error;
699 }
700
701 static void
702 ckpt_handler(struct proc *p) 
703 {
704         char *buf;
705         struct file *fp;
706         int error;
707
708         chptinuse++;
709
710         /*
711          * Being able to checkpoint an suid or sgid program is not a good
712          * idea.
713          */
714         if (sugid_coredump == 0 && (p->p_flag & P_SUGID)) {
715                 chptinuse--;
716                 return;
717         }
718
719         buf = ckpt_expand_name(p->p_comm, p->p_ucred->cr_uid, p->p_pid);
720         if (buf == NULL) {
721                 chptinuse--;
722                 return;
723         }
724
725         log(LOG_INFO, "pid %d (%s), uid %d: checkpointing to %s\n",
726                 p->p_pid, p->p_comm, 
727                 (p->p_ucred ? p->p_ucred->cr_uid : -1),
728                 buf);
729
730         PRINTF(("ckpt handler called, using '%s'\n", buf));
731
732         /*
733          * Use the same safety flags that the coredump code uses.
734          */
735         error = fp_open(buf, O_WRONLY|O_CREAT|O_NOFOLLOW, 0600, &fp);
736         if (error == 0) {
737                 (void)ckpt_freeze_proc(p, fp);
738                 fp_close(fp);
739         } else {
740                 printf("checkpoint failed with open - error: %d\n", error);
741         }
742         free(buf, M_TEMP);
743         chptinuse--;
744 }
745
746
747 /*
748  * The `sysent' for the new syscall
749  */
750 static struct sysent ckpt_sysent = {
751         4,              /* sy_narg */
752         ckpt_proc       /* sy_call */
753 };
754
755 static int ckpt_offset = 210;
756
757 static int
758 load (struct module *module, int cmd, void *arg)
759 {
760         int error = 0;
761        
762         switch (cmd) {
763         case MOD_LOAD :         
764                 PRINTF( ("ckpt loaded at %d\n", ckpt_offset));
765                 (void)register_ckpt_func(ckpt_handler);
766                 break;
767         case MOD_UNLOAD :
768                 if (chptinuse) {
769                         error = EBUSY;
770                         PRINTF(("chpt in progress, unable to unload ckpt module\n"));
771                 }
772                 else {
773                         (void)register_ckpt_func(NULL);
774                         PRINTF(("ckpt unloaded from %d\n", ckpt_offset));
775                 }
776                 break;
777         default :
778                 error = EINVAL;
779                 break;
780         }
781         return error;
782 }
783
784 SYSCALL_MODULE(syscall, &ckpt_offset, &ckpt_sysent, load, NULL);