Beispiel #1
0
func main() {
	args := Arguments()

	infile, args, err := args.String("in-file", nil)
	Check(err)
	outfile, args, err := args.String("out-file", nil)
	Check(err)

	infd, err := syscall.Open(infile, int(ReadOnly), 0)
	Check(err)
	defer func() { Check(syscall.Close(infd)) }()

	flags := Create | WriteOnly | Truncate
	perms := 0 |
		UserRead | UserWrite |
		GroupRead | GroupWrite |
		OtherRead | OtherWrite
	outfd, err := syscall.Open(outfile, int(flags), uint32(perms))
	Check(err)
	defer func() { Check(syscall.Close(outfd)) }()

	bufSize := 1024
	buf := make([]byte, bufSize)
	var n int
	for {
		n, err = syscall.Read(infd, buf)
		Check(err)
		if n == 0 {
			break
		}
		_, err = syscall.Write(outfd, buf[:n])
		Check(err)
	}
}
Beispiel #2
0
func (s *cScreen) Init() error {

	s.evch = make(chan Event, 2)
	s.quit = make(chan struct{})

	if in, e := syscall.Open("CONIN$", syscall.O_RDWR, 0); e != nil {
		return e
	} else {
		s.in = in
	}
	if out, e := syscall.Open("CONOUT$", syscall.O_RDWR, 0); e != nil {
		syscall.Close(s.in)
		return e
	} else {
		s.out = out
	}

	s.curx = -1
	s.cury = -1
	s.getCursorInfo(&s.ocursor)
	s.getConsoleInfo(&s.oscreen)
	s.getOutMode(&s.oomode)
	s.getInMode(&s.oimode)
	s.resize()

	s.setInMode(modeResizeEn)
	s.setOutMode(0)
	s.clearScreen(s.style)
	s.hideCursor()
	go s.scanInput()

	return nil
}
Beispiel #3
0
func afs_syscall(call uintptr, param1 uintptr, param2 uintptr, param3 uintptr, param4 uintptr) error {
	fd, err := syscall.Open("/proc/fs/openafs/afs_ioctl", syscall.O_RDWR, 0)

	if err != nil {
		fd, err = syscall.Open("/proc/fs/nnpfs/afs_ioctl", syscall.O_RDWR, 0)
	}

	if err != nil {
		return err
	}

	data := afsprocdata{
		param4,
		param3,
		param2,
		param1,
		call,
	}

	err = gioctl.Ioctl(uintptr(fd), gioctl.IoW(67, 1, unsafe.Sizeof(uintptr(0))), uintptr(unsafe.Pointer(&data)))

	syscall.Close(fd)

	return err
}
Beispiel #4
0
func (constor *Constor) Write(input *fuse.WriteIn, data []byte) (written uint32, code fuse.Status) {
	constor.log("%d", input.Fh)
	ptr := uintptr(input.Fh)
	offset := input.Offset

	F := constor.getfd(ptr)
	if F == nil {
		constor.error("F == nil")
		return 0, fuse.EIO
	}
	inode, err := constor.inodemap.findInode(input.NodeId)
	if err != nil {
		constor.error("%s", err)
		return 0, fuse.ToStatus(err)
	}
	if F.layer != 0 && inode.layer != 0 {
		err = constor.copyup(inode)
		if err != nil {
			constor.error("%s", err)
			return 0, fuse.ToStatus(err)
		}
		path, err := constor.dentrymap.getPath(inode.ino)
		if err != nil {
			constor.error("%s", err)
			return 0, fuse.ToStatus(err)
		}
		pathl := Path.Join(constor.layers[0], path)
		syscall.Close(F.fd)
		fd, err := syscall.Open(pathl, F.flags, 0)
		if err != nil {
			constor.error("%s", err)
			return 0, fuse.ToStatus(err)
		}
		F.fd = fd
		F.layer = 0
		constor.log("reset fd for %s", path)
	} else if F.layer != 0 && inode.layer == 0 {
		syscall.Close(F.fd)
		path, err := constor.dentrymap.getPath(inode.ino)
		if err != nil {
			constor.error("%s", err)
			return 0, fuse.ToStatus(err)
		}
		pathl := Path.Join(constor.layers[0] + path)
		fd, err := syscall.Open(pathl, F.flags, 0)
		if err != nil {
			constor.error("%s", err)
			return 0, fuse.ToStatus(err)
		}
		F.fd = fd
		F.layer = 0
		constor.log("reset fd for %s", path)
	}

	fd := F.fd
	n, err := syscall.Pwrite(fd, data, int64(offset))
	return uint32(n), fuse.ToStatus(err)
}
Beispiel #5
0
// Initializes termbox library. This function should be called before any other functions.
// After successful initialization, the library must be finalized using 'Close' function.
//
// Example usage:
//      err := termbox.Init()
//      if err != nil {
//              panic(err)
//      }
//      defer termbox.Close()
func Init() error {
	var err error

	interrupt, err = create_event()
	if err != nil {
		return err
	}

	in, err = syscall.Open("CONIN$", syscall.O_RDWR, 0)
	if err != nil {
		return err
	}
	out, err = syscall.Open("CONOUT$", syscall.O_RDWR, 0)
	if err != nil {
		return err
	}

	err = get_console_mode(in, &orig_mode)
	if err != nil {
		return err
	}

	err = set_console_mode(in, enable_window_input)
	if err != nil {
		return err
	}

	orig_size = get_term_size(out)
	win_size := get_win_size(out)

	err = set_console_screen_buffer_size(out, win_size)
	if err != nil {
		return err
	}

	err = get_console_cursor_info(out, &orig_cursor_info)
	if err != nil {
		return err
	}

	show_cursor(false)
	term_size = get_term_size(out)
	back_buffer.init(int(term_size.x), int(term_size.y))
	front_buffer.init(int(term_size.x), int(term_size.y))
	back_buffer.clear()
	front_buffer.clear()
	clear()

	diffbuf = make([]diff_msg, 0, 32)

	go input_event_producer()
	IsInit = true
	return nil
}
Beispiel #6
0
// pivotRoot will call pivot_root such that rootfs becomes the new root
// filesystem, and everything else is cleaned up.
func pivotRoot(rootfs string) error {
	// While the documentation may claim otherwise, pivot_root(".", ".") is
	// actually valid. What this results in is / being the new root but
	// /proc/self/cwd being the old root. Since we can play around with the cwd
	// with pivot_root this allows us to pivot without creating directories in
	// the rootfs. Shout-outs to the LXC developers for giving us this idea.

	oldroot, err := syscall.Open("/", syscall.O_DIRECTORY|syscall.O_RDONLY, 0)
	if err != nil {
		return err
	}
	defer syscall.Close(oldroot)

	newroot, err := syscall.Open(rootfs, syscall.O_DIRECTORY|syscall.O_RDONLY, 0)
	if err != nil {
		return err
	}
	defer syscall.Close(newroot)

	// Change to the new root so that the pivot_root actually acts on it.
	if err := syscall.Fchdir(newroot); err != nil {
		return err
	}

	if err := syscall.PivotRoot(".", "."); err != nil {
		return fmt.Errorf("pivot_root %s", err)
	}

	// Currently our "." is oldroot (according to the current kernel code).
	// However, purely for safety, we will fchdir(oldroot) since there isn't
	// really any guarantee from the kernel what /proc/self/cwd will be after a
	// pivot_root(2).

	if err := syscall.Fchdir(oldroot); err != nil {
		return err
	}

	// Make oldroot rprivate to make sure our unmounts don't propagate to the
	// host (and thus bork the machine).
	if err := syscall.Mount("", ".", "", syscall.MS_PRIVATE|syscall.MS_REC, ""); err != nil {
		return err
	}
	// Preform the unmount. MNT_DETACH allows us to unmount /proc/self/cwd.
	if err := syscall.Unmount(".", syscall.MNT_DETACH); err != nil {
		return err
	}

	// Switch back to our shiny new root.
	if err := syscall.Chdir("/"); err != nil {
		return fmt.Errorf("chdir / %s", err)
	}
	return nil
}
Beispiel #7
0
func (constor *Constor) Write(input *fuse.WriteIn, data []byte) (written uint32, code fuse.Status) {
	constor.log("%d %d", input.Fh, len(data))
	ptr := uintptr(input.Fh)
	offset := input.Offset
	wdata := data

	F := constor.getfd(ptr)
	if F == nil {
		constor.error("F == nil")
		return 0, fuse.EIO
	}
	if F.flags&syscall.O_DIRECT != 0 {
		wdata = directio.AlignedBlock(len(data))
		copy(wdata, data)
	}
	inode := constor.inodemap.findInodePtr(input.NodeId)
	if inode == nil {
		return 0, fuse.ENOENT
	}
	if F.layer != 0 && inode.layer != 0 {
		err := constor.copyup(inode)
		if err != nil {
			constor.error("%s", err)
			return 0, fuse.ToStatus(err)
		}
		path := constor.getPath(0, inode.id)
		syscall.Close(F.fd)
		fd, err := syscall.Open(path, F.flags, 0)
		if err != nil {
			constor.error("%s", err)
			return 0, fuse.ToStatus(err)
		}
		F.fd = fd
		F.layer = 0
		constor.log("reset fd for %s", path)
	} else if F.layer != 0 && inode.layer == 0 {
		syscall.Close(F.fd)
		path := constor.getPath(0, inode.id)
		fd, err := syscall.Open(path, F.flags, 0)
		if err != nil {
			constor.error("%s", err)
			return 0, fuse.ToStatus(err)
		}
		F.fd = fd
		F.layer = 0
		constor.log("reset fd for %s", path)
	}

	fd := F.fd
	n, err := syscall.Pwrite(fd, wdata, int64(offset))
	return uint32(n), fuse.ToStatus(err)
}
Beispiel #8
0
func main() {
	var err error
	var b []byte
	var l uintptr

	flag.Parse()
	if len(flag.Args()) < 1 {
		flag.PrintDefaults()
		log.Fatalf("usage: kexec [flags] kernelname")
	}
	kernel := flag.Args()[0]

	if *cmdline != "" {
		b = append(b, []byte(*cmdline)...)
		l = uintptr(len(b)) + 1
	} else {
		b, err = ioutil.ReadFile("/proc/cmdline")
		if err != nil {
			log.Fatalf("%v", err)
		}
		b[len(b)-1] = 0
		l = uintptr(len(b))
	}

	p := uintptr(unsafe.Pointer(&b[0]))

	log.Printf("Loading %v\n", kernel)

	if kern, err = syscall.Open(kernel, syscall.O_RDONLY, 0); err != nil {
		log.Fatalf("%v", err)
	}

	if ramfs, err = syscall.Open(*initramfs, syscall.O_RDONLY, 0); err != nil {
		flags |= KEXEC_FILE_NO_INITRAMFS
	}

	log.Printf("command line: '%v'", string(b))
	log.Printf("%v %v %v %v %v %v", 320, uintptr(kern), uintptr(ramfs), p, l, flags)
	if *dryrun {
		log.Printf("Dry run -- exiting now")
		return
	}
	if e1, e2, err := syscall.Syscall6(320, uintptr(kern), uintptr(ramfs), l, p, flags, uintptr(0)); err != 0 {
		log.Fatalf("a %v b %v err %v", e1, e2, err)
	}

	if e1, e2, err := syscall.Syscall6(syscall.SYS_REBOOT, syscall.LINUX_REBOOT_MAGIC1, syscall.LINUX_REBOOT_MAGIC2, syscall.LINUX_REBOOT_CMD_KEXEC, 0, 0, 0); err != 0 {
		log.Fatalf("a %v b %v err %v", e1, e2, err)
	}
}
Beispiel #9
0
func OpenFile(name string, mode int, perm uint32) (file *File, err os.Error) {
	r, e := syscall.Open(name, mode, perm)
	if e != 0 {
		err = os.Errno(e)
	}
	return newFile(r, name), err
}
Beispiel #10
0
// NewLock opens a new lock on a file without acquisition
func NewLock(path string, lockType LockType) (*FileLock, error) {
	l := &FileLock{path: path, fd: -1}

	mode := syscall.O_RDONLY | syscall.O_CLOEXEC
	if lockType == Dir {
		mode |= syscall.O_DIRECTORY
	}
	lfd, err := syscall.Open(l.path, mode, 0)
	if err != nil {
		if err == syscall.ENOENT {
			err = ErrNotExist
		} else if err == syscall.EACCES {
			err = ErrPermission
		}
		return nil, err
	}
	l.fd = lfd

	var stat syscall.Stat_t
	err = syscall.Fstat(lfd, &stat)
	if err != nil {
		return nil, err
	}
	// Check if the file is a regular file
	if lockType == RegFile && !(stat.Mode&syscall.S_IFMT == syscall.S_IFREG) {
		return nil, ErrNotRegular
	}

	return l, nil
}
Beispiel #11
0
func Open() (*Fifo, error) {
	var pipe Fifo
	pipe.path = defaultPath
	if config.Has("global.fifo") {
		pipe.path, _ = config.String("config.fifo")
	}

	if _, err := os.Stat(pipe.path); err == nil {
		err = os.Remove(pipe.path)
		if err != nil {
			return nil, err
		}
	}

	if err := syscall.Mkfifo(pipe.path, 0777); err != nil {
		return nil, err
	}

	fd, err := syscall.Open(pipe.path, syscall.O_RDONLY|syscall.O_NONBLOCK, 0777)
	if err != nil {
		return nil, err
	}

	pipe.fd = fdReader(fd)
	pipe.rd = bufio.NewReader(pipe.fd)
	pipe.cmds = make([]Command, 0, 5)
	return &pipe, nil
}
Beispiel #12
0
func init_disk_usage(disk_usage *DiskUsage) bool {
	if disk_usage.reserved_blocks == 0 {
		disk_usage.reserved_blocks = DEFAULT_FS_RESERVED_BLOCKS
	}
	disk_usage.total_size = 0
	disk_usage.static_size = 0
	disk_usage.dynamic_size = 0
	disk_usage.ratio = 0.0

	base_dir := "/store/instance"
	fd, err := syscall.Open(base_dir, syscall.O_RDONLY, 0x664)
	if err != nil {
		logger.Error("%s does not exist, ignore disk quota filter.", base_dir)
		return false
	}
	defer syscall.Close(fd)

	var statfs syscall.Statfs_t
	err = syscall.Fstatfs(fd, &statfs)
	if err != nil {
		logger.Error("Failed to get %s file system stats [%s].", base_dir, err)
		return false
	}

	total_size := uint64(statfs.Bsize) * uint64(float64(statfs.Blocks)*
		float64(1.0-disk_usage.reserved_blocks))
	logger.Debug("Get total disk size %d.", total_size)
	disk_usage.total_size = total_size
	return true
}
Beispiel #13
0
func NewBone() (Bone, error) {
	b, err := syscall.Open("/dev/logibone_mem", syscall.O_RDWR|syscall.O_SYNC, 777)
	if err != nil {
		return 0, err
	}
	return Bone(b), nil
}
Beispiel #14
0
func openFile(name string, flag int, perm FileMode) (file *File, err error) {
	r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
	if e != nil {
		return nil, &PathError{"open", name, e}
	}
	return NewFile(uintptr(r), name), nil
}
Beispiel #15
0
func OpenFile(name string, mode int, perm uint32) (file *File, err error) {
	r, e := syscall.Open(name, mode, perm)
	if e != nil {
		err = e
	}
	return newFile(r, name), err
}
func addFilesToKqueue(evTrackList []syscall.Kevent_t, dirPath string) {
	fileList, err := ioutil.ReadDir(dirPath)
	if err != nil {
		log.Fatal("ReadDir failed: ", err)
	}
	for _, file := range fileList {
		newPath := dirPath + "/" + file.Name()
		if file.IsDir() && file.Name() != ".git" {
			addFilesToPoll(newPath)
		} else {
			fileName := file.Name()
			if len(strings.Split(fileName, ".")) > 1 {
				fileExt := strings.Split(fileName, ".")[1]
				if fileExt == ext {
					fd, err := syscall.Open(newPath, syscall.O_RDONLY, 0)
					if err != nil {
						log.Fatal("Open failed: ", err)
					}
					// build kevent
					ev := syscall.Kevent_t{
						Ident:  uint64(fd),
						Filter: syscall.EVFILT_VNODE,
						Flags:  syscall.EV_ADD | syscall.EV_ENABLE | syscall.EV_ONESHOT,
						Fflags: syscall.NOTE_DELETE | syscall.NOTE_WRITE,
						Data:   0,
						Udata:  nil,
					}
					evTrackList = append(evTrackList, ev)
				}
			}
		}
	}
}
Beispiel #17
0
// watch starts to watch given p file/directory.
func (k *kqueue) singlewatch(p string, e Event, direct bool,
	fi os.FileInfo) error {
	w, ok := k.pthLkp[p]
	if !ok {
		fd, err := syscall.Open(p, syscall.O_NONBLOCK|syscall.O_RDONLY, 0)
		if err != nil {
			return err
		}
		w = &watched{fd: fd, p: p, fi: fi}
	}
	if direct {
		w.eDir |= e
	} else {
		w.eNonDir |= e
	}
	var kevn [1]syscall.Kevent_t
	syscall.SetKevent(&kevn[0], w.fd, syscall.EVFILT_VNODE,
		syscall.EV_ADD|syscall.EV_CLEAR)
	kevn[0].Fflags = encode(w.eDir | w.eNonDir)
	if _, err := syscall.Kevent(k.fd, kevn[:], nil, nil); err != nil {
		return err
	}
	if !ok {
		k.idLkp[w.fd], k.pthLkp[w.p] = w, w
		return nil
	}
	return errAlreadyWatched
}
func exclusiveOpenFailsOnDevice(pathname string) (bool, error) {
	isDevice, err := pathIsDevice(pathname)
	if err != nil {
		return false, fmt.Errorf(
			"PathIsDevice failed for path %q: %v",
			pathname,
			err)
	}
	if !isDevice {
		glog.Errorf("Path %q is not refering to a device.", pathname)
		return false, nil
	}
	fd, errno := syscall.Open(pathname, syscall.O_RDONLY|syscall.O_EXCL, 0)
	// If the device is in use, open will return an invalid fd.
	// When this happens, it is expected that Close will fail and throw an error.
	defer syscall.Close(fd)
	if errno == nil {
		// device not in use
		return false, nil
	} else if errno == syscall.EBUSY {
		// device is in use
		return true, nil
	}
	// error during call to Open
	return false, errno
}
Beispiel #19
0
// Pty returns a UNIX 98 pseudoterminal device.
// Pty returns a pair of fds representing the master and slave pair.
func Pty() (*os.File, *os.File, error) {
	open := func(path string) (uintptr, error) {
		fd, err := syscall.Open(path, syscall.O_NOCTTY|syscall.O_RDWR|syscall.O_CLOEXEC, 0666)
		if err != nil {
			return 0, fmt.Errorf("unable to open %q: %v", path, err)
		}
		return uintptr(fd), nil
	}

	ptm, err := open("/dev/ptmx")
	if err != nil {
		return nil, nil, err
	}

	sname, err := ptsname(ptm)
	if err != nil {
		return nil, nil, err
	}

	err = grantpt(ptm)
	if err != nil {
		return nil, nil, err
	}

	err = unlockpt(ptm)
	if err != nil {
		return nil, nil, err
	}

	pts, err := open(sname)
	if err != nil {
		return nil, nil, err
	}
	return os.NewFile(uintptr(ptm), "ptm"), os.NewFile(uintptr(pts), sname), nil
}
Beispiel #20
0
func open(name string) (uintptr, error) {
	fd, err := syscall.Open(name, syscall.O_RDONLY, 0)
	if err != nil {
		return 0, err
	}
	return uintptr(fd), nil
}
Beispiel #21
0
// OpenTerminal is a clone of os.OpenFile without the O_CLOEXEC
// used to open the pty slave inside the container namespace
func OpenTerminal(name string, flag int) (*os.File, error) {
	r, e := syscall.Open(name, flag, 0)
	if e != nil {
		return nil, &os.PathError{Op: "open", Path: name, Err: e}
	}
	return os.NewFile(uintptr(r), name), nil
}
Beispiel #22
0
// GetFromName gets a handle to a named network namespace such as one
// created by `ip netns add`.
func GetFromName(name string) (NsHandle, error) {
	fd, err := syscall.Open(fmt.Sprintf("/var/run/netns/%s", name), syscall.O_RDONLY, 0)
	if err != nil {
		return -1, err
	}
	return NsHandle(fd), nil
}
Beispiel #23
0
// GetFromName gets a handle to the network namespace of a given pid.
func GetFromPid(pid int) (NsHandle, error) {
	fd, err := syscall.Open(fmt.Sprintf("/proc/%d/ns/net", pid), syscall.O_RDONLY, 0)
	if err != nil {
		return -1, err
	}
	return NsHandle(fd), nil
}
Beispiel #24
0
// GetFromPath gets a handle to a network namespace
// identified by the path
func GetFromPath(path string) (NsHandle, error) {
	fd, err := syscall.Open(path, syscall.O_RDONLY, 0)
	if err != nil {
		return -1, err
	}
	return NsHandle(fd), nil
}
Beispiel #25
0
func kmemleakScan(report bool) {
	fd, err := syscall.Open("/sys/kernel/debug/kmemleak", syscall.O_RDWR, 0)
	if err != nil {
		panic(err)
	}
	defer syscall.Close(fd)
	if _, err := syscall.Write(fd, []byte("scan")); err != nil {
		panic(err)
	}
	if report {
		if kmemleakBuf == nil {
			kmemleakBuf = make([]byte, 128<<10)
		}
		n, err := syscall.Read(fd, kmemleakBuf)
		if err != nil {
			panic(err)
		}
		if n != 0 {
			// BUG in output should be recognized by manager.
			logf(0, "BUG: memory leak:\n%s\n", kmemleakBuf[:n])
		}
	}
	if _, err := syscall.Write(fd, []byte("clear")); err != nil {
		panic(err)
	}
}
Beispiel #26
0
// Open the named path for reading, writing or both, depnding on the
// flags argument.
func Open(name string, flags int) (*FD, error) {
	sysfd, err := syscall.Open(name, flags, o_MODE)
	if err != nil {
		return nil, err
	}
	return newFD(sysfd, name)
}
Beispiel #27
0
func init() {
	switch path, _ := syscall.Getenv("GOCOVOUT"); path {
	case "":
		// No tracing
	case "-":
		Default.Tracer = fdwriter(syscall.Stdout)
	default:
		// Add the process ID to the filename.
		// TODO handle PID reuse by checking if file exists.
		path += "." + itoa(syscall.Getpid())

		mode := syscall.O_WRONLY | syscall.O_CREAT | syscall.O_TRUNC
		fd, err := syscall.Open(path, mode, 0666)
		if err != nil {
			msg := "gocov: failed to create log file: "
			msg += err.Error() + "\n"
			write(fdwriter(syscall.Stderr), []byte(msg))
			syscall.Exit(1)
		}
		Default.Tracer = fdwriter(int(fd))
	}

	// Remove GOCOVOUT from environ, to prevent noise from child processes.
	// TODO Don't do this; append .pid to output filename.
	syscall.Setenv("GOCOVOUT", "")
}
Beispiel #28
0
// NewWatched implements trigger.
func (*kq) NewWatched(p string, fi os.FileInfo) (*watched, error) {
	fd, err := syscall.Open(p, syscall.O_NONBLOCK|syscall.O_RDONLY, 0)
	if err != nil {
		return nil, err
	}
	return &watched{fd: fd, p: p, fi: fi}, nil
}
Beispiel #29
0
// OpenFile is the generalized open call; most users will use Open
// or Create instead.  It opens the named file with specified flag
// (O_RDONLY etc.) and perm, (0666 etc.) if applicable.  If successful,
// methods on the returned File can be used for I/O.
// If there is an error, it will be of type *PathError.
func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {
	chmod := false
	if !supportsCreateWithStickyBit && flag&O_CREATE != 0 && perm&ModeSticky != 0 {
		if _, err := Stat(name); IsNotExist(err) {
			chmod = true
		}
	}

	r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
	if e != nil {
		return nil, &PathError{"open", name, e}
	}

	// open(2) itself won't handle the sticky bit on *BSD and Solaris
	if chmod {
		Chmod(name, perm)
	}

	// There's a race here with fork/exec, which we are
	// content to live with.  See ../syscall/exec_unix.go.
	if !supportsCloseOnExec {
		syscall.CloseOnExec(r)
	}

	return NewFile(uintptr(r), name), nil
}
Beispiel #30
0
func init() {
	var err error
	hConin, err = syscall.Open("CONIN$", syscall.O_RDWR, 0)
	if err != nil {
		panic(fmt.Sprintf("conio: %v", err))
	}
}