diff options
Diffstat (limited to 'libgo/syscalls')
-rw-r--r-- | libgo/syscalls/exec.go | 101 | ||||
-rw-r--r-- | libgo/syscalls/netlink_linux.go | 227 | ||||
-rw-r--r-- | libgo/syscalls/socket.go | 4 | ||||
-rw-r--r-- | libgo/syscalls/socket_bsd.go | 4 | ||||
-rw-r--r-- | libgo/syscalls/socket_irix.go | 6 | ||||
-rw-r--r-- | libgo/syscalls/socket_linux.go | 90 | ||||
-rw-r--r-- | libgo/syscalls/socket_solaris.go | 4 | ||||
-rw-r--r-- | libgo/syscalls/syscall_linux.go | 18 |
8 files changed, 440 insertions, 14 deletions
diff --git a/libgo/syscalls/exec.go b/libgo/syscalls/exec.go index 450c7e5..04d0ef8 100644 --- a/libgo/syscalls/exec.go +++ b/libgo/syscalls/exec.go @@ -13,8 +13,14 @@ import "unsafe" func libc_fcntl(fd int, cmd int, arg int) int __asm__ ("fcntl") func libc_fork() Pid_t __asm__ ("fork") func libc_setsid() Pid_t __asm__ ("setsid") +func libc_setpgid(Pid_t, Pid_t) int __asm__ ("setpgid") +func libc_chroot(path *byte) int __asm__ ("chroot") +func libc_setuid(Uid_t) int __asm__ ("setuid") +func libc_setgid(Gid_t) int __asm__ ("setgid") +func libc_setgroups(Size_t, *Gid_t) int __asm__ ("setgroups") func libc_chdir(name *byte) int __asm__ ("chdir") func libc_dup2(int, int) int __asm__ ("dup2") +func libc_ioctl(int, int) int __asm__ ("ioctl") func libc_execve(*byte, **byte, **byte) int __asm__ ("execve") func libc_sysexit(int) __asm__ ("_exit") @@ -24,7 +30,7 @@ func libc_sysexit(int) __asm__ ("_exit") // In the child, this function must not acquire any locks, because // they might have been locked at the time of the fork. This means // no rescheduling, no malloc calls, and no new stack segments. -func forkAndExecInChild(argv0 *byte, argv, envv []*byte, dir *byte, attr *ProcAttr, pipe int) (pid int, err int) { +func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err int) { // Declare all variables at top in case any // declarations require heap allocation (e.g., err1). var r1, r2, err1 uintptr @@ -51,19 +57,51 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, dir *byte, attr *ProcAt // Fork succeeded, now in child. // Enable tracing if requested. - if attr.Ptrace { + if sys.Ptrace { if libc_ptrace(_PTRACE_TRACEME, 0, 0, nil) < 0 { goto childerror } } // Session ID - if attr.Setsid { + if sys.Setsid { if libc_setsid() == Pid_t(-1) { goto childerror } } + // Set process group + if sys.Setpgid { + if libc_setpgid(0, 0) < 0 { + goto childerror + } + } + + // Chroot + if chroot != nil { + if libc_chroot(chroot) < 0 { + goto childerror + } + } + + // User and groups + if cred := sys.Credential; cred != nil { + ngroups := uintptr(len(cred.Groups)) + var groups *Gid_t + if ngroups > 0 { + groups = (*Gid_t)(unsafe.Pointer(&cred.Groups[0])) + } + if libc_setgroups(Size_t(ngroups), groups) < 0 { + goto childerror + } + if libc_setgid(Gid_t(cred.Gid)) < 0 { + goto childerror + } + if libc_setuid(Uid_t(cred.Uid)) < 0 { + goto childerror + } + } + // Chdir if dir != nil { if libc_chdir(dir) < 0 { @@ -129,6 +167,20 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, dir *byte, attr *ProcAt libc_close(i) } + // Detach fd 0 from tty + if sys.Noctty { + if libc_ioctl(0, TIOCNOTTY) < 0 { + goto childerror + } + } + + // Make fd 0 the tty + if sys.Setctty { + if libc_ioctl(0, TIOCSCTTY) < 0 { + goto childerror + } + } + // Time to exec. libc_execve(argv0, &argv[0], &envv[0]) @@ -147,16 +199,35 @@ childerror: panic("unreached") } +// Credential holds user and group identities to be assumed +// by a child process started by StartProcess. +type Credential struct { + Uid uint32 // User ID. + Gid uint32 // Group ID. + Groups []uint32 // Supplementary group IDs. +} +// ProcAttr holds attributes that will be applied to a new process started +// by StartProcess. type ProcAttr struct { - Setsid bool // Create session. - Ptrace bool // Enable tracing. - Dir string // Current working directory. - Env []string // Environment. - Files []int // File descriptors. + Dir string // Current working directory. + Env []string // Environment. + Files []int // File descriptors. + Sys *SysProcAttr } -var zeroAttributes ProcAttr +type SysProcAttr struct { + Chroot string // Chroot. + Credential *Credential // Credential. + Ptrace bool // Enable tracing. + Setsid bool // Create session. + Setpgid bool // Set process group ID to new pid (SYSV setpgrp) + Setctty bool // Set controlling terminal to fd 0 + Noctty bool // Detach fd 0 from controlling terminal +} + +var zeroProcAttr ProcAttr +var zeroSysProcAttr SysProcAttr func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { var p [2]int @@ -165,7 +236,11 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { var wstatus WaitStatus if attr == nil { - attr = &zeroAttributes + attr = &zeroProcAttr + } + sys := attr.Sys + if sys == nil { + sys = &zeroSysProcAttr } p[0] = -1 @@ -180,6 +255,10 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { argvp[0] = argv0p } + var chroot *byte + if sys.Chroot != "" { + chroot = StringBytePtr(sys.Chroot) + } var dir *byte if attr.Dir != "" { dir = StringBytePtr(attr.Dir) @@ -202,7 +281,7 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { } // Kick off child. - pid, err = forkAndExecInChild(argv0p, argvp, envvp, dir, attr, p[1]) + pid, err = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, sys, p[1]) if err != 0 { error: if p[0] >= 0 { diff --git a/libgo/syscalls/netlink_linux.go b/libgo/syscalls/netlink_linux.go new file mode 100644 index 0000000..6f621ce --- /dev/null +++ b/libgo/syscalls/netlink_linux.go @@ -0,0 +1,227 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Netlink sockets and messages + +package syscall + +import ( + "unsafe" +) + +// Round the length of a netlink message up to align it properly. +func nlmAlignOf(msglen int) int { + return (msglen + NLMSG_ALIGNTO - 1) & ^(NLMSG_ALIGNTO - 1) +} + +// Round the length of a netlink route attribute up to align it +// properly. +func rtaAlignOf(attrlen int) int { + return (attrlen + RTA_ALIGNTO - 1) & ^(RTA_ALIGNTO - 1) +} + +// NetlinkRouteRequest represents the request message to receive +// routing and link states from the kernel. +type NetlinkRouteRequest struct { + Header NlMsghdr + Data RtGenmsg +} + +func (rr *NetlinkRouteRequest) toWireFormat() []byte { + b := make([]byte, rr.Header.Len) + b[0] = byte(rr.Header.Len) + b[1] = byte(rr.Header.Len >> 8) + b[2] = byte(rr.Header.Len >> 16) + b[3] = byte(rr.Header.Len >> 24) + b[4] = byte(rr.Header.Type) + b[5] = byte(rr.Header.Type >> 8) + b[6] = byte(rr.Header.Flags) + b[7] = byte(rr.Header.Flags >> 8) + b[8] = byte(rr.Header.Seq) + b[9] = byte(rr.Header.Seq >> 8) + b[10] = byte(rr.Header.Seq >> 16) + b[11] = byte(rr.Header.Seq >> 24) + b[12] = byte(rr.Header.Pid) + b[13] = byte(rr.Header.Pid >> 8) + b[14] = byte(rr.Header.Pid >> 16) + b[15] = byte(rr.Header.Pid >> 24) + b[16] = byte(rr.Data.Family) + return b +} + +func newNetlinkRouteRequest(proto, seq, family int) []byte { + rr := &NetlinkRouteRequest{} + rr.Header.Len = uint32(NLMSG_HDRLEN + SizeofRtGenmsg) + rr.Header.Type = uint16(proto) + rr.Header.Flags = NLM_F_DUMP | NLM_F_REQUEST + rr.Header.Seq = uint32(seq) + rr.Data.Family = uint8(family) + return rr.toWireFormat() +} + +// NetlinkRIB returns routing information base, as known as RIB, +// which consists of network facility information, states and +// parameters. +func NetlinkRIB(proto, family int) ([]byte, int) { + var ( + s int + e int + lsanl SockaddrNetlink + seq int + tab []byte + ) + + s, e = Socket(AF_NETLINK, SOCK_RAW, 0) + if e != 0 { + return nil, e + } + defer Close(s) + + lsanl.Family = AF_NETLINK + e = Bind(s, &lsanl) + if e != 0 { + return nil, e + } + + seq++ + wb := newNetlinkRouteRequest(proto, seq, family) + e = Sendto(s, wb, 0, &lsanl) + if e != 0 { + return nil, e + } + + for { + var ( + rb []byte + nr int + lsa Sockaddr + ) + + rb = make([]byte, Getpagesize()) + nr, _, e = Recvfrom(s, rb, 0) + if e != 0 { + return nil, e + } + if nr < NLMSG_HDRLEN { + return nil, EINVAL + } + rb = rb[:nr] + tab = append(tab, rb...) + + msgs, _ := ParseNetlinkMessage(rb) + for _, m := range msgs { + if lsa, e = Getsockname(s); e != 0 { + return nil, e + } + switch v := lsa.(type) { + case *SockaddrNetlink: + if m.Header.Seq != uint32(seq) || m.Header.Pid != v.Pid { + return nil, EINVAL + } + default: + return nil, EINVAL + } + if m.Header.Type == NLMSG_DONE { + goto done + } + if m.Header.Type == NLMSG_ERROR { + return nil, EINVAL + } + } + } + +done: + return tab, 0 +} + +// NetlinkMessage represents the netlink message. +type NetlinkMessage struct { + Header NlMsghdr + Data []byte +} + +// ParseNetlinkMessage parses buf as netlink messages and returns +// the slice containing the NetlinkMessage structs. +func ParseNetlinkMessage(buf []byte) ([]NetlinkMessage, int) { + var ( + h *NlMsghdr + dbuf []byte + dlen int + e int + msgs []NetlinkMessage + ) + + for len(buf) >= NLMSG_HDRLEN { + h, dbuf, dlen, e = netlinkMessageHeaderAndData(buf) + if e != 0 { + break + } + m := NetlinkMessage{} + m.Header = *h + m.Data = dbuf[:int(h.Len)-NLMSG_HDRLEN] + msgs = append(msgs, m) + buf = buf[dlen:] + } + + return msgs, e +} + +func netlinkMessageHeaderAndData(buf []byte) (*NlMsghdr, []byte, int, int) { + h := (*NlMsghdr)(unsafe.Pointer(&buf[0])) + if int(h.Len) < NLMSG_HDRLEN || int(h.Len) > len(buf) { + return nil, nil, 0, EINVAL + } + return h, buf[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), 0 +} + +// NetlinkRouteAttr represents the netlink route attribute. +type NetlinkRouteAttr struct { + Attr RtAttr + Value []byte +} + +// ParseNetlinkRouteAttr parses msg's payload as netlink route +// attributes and returns the slice containing the NetlinkRouteAttr +// structs. +func ParseNetlinkRouteAttr(msg *NetlinkMessage) ([]NetlinkRouteAttr, int) { + var ( + buf []byte + a *RtAttr + alen int + vbuf []byte + e int + attrs []NetlinkRouteAttr + ) + + switch msg.Header.Type { + case RTM_NEWLINK: + buf = msg.Data[SizeofIfInfomsg:] + case RTM_NEWADDR: + buf = msg.Data[SizeofIfAddrmsg:] + default: + return nil, EINVAL + } + + for len(buf) >= SizeofRtAttr { + a, vbuf, alen, e = netlinkRouteAttrAndValue(buf) + if e != 0 { + break + } + ra := NetlinkRouteAttr{} + ra.Attr = *a + ra.Value = vbuf[:int(a.Len)-SizeofRtAttr] + attrs = append(attrs, ra) + buf = buf[alen:] + } + + return attrs, 0 +} + +func netlinkRouteAttrAndValue(buf []byte) (*RtAttr, []byte, int, int) { + h := (*RtAttr)(unsafe.Pointer(&buf[0])) + if int(h.Len) < SizeofRtAttr || int(h.Len) > len(buf) { + return nil, nil, 0, EINVAL + } + return h, buf[SizeofRtAttr:], rtaAlignOf(int(h.Len)), 0 +} diff --git a/libgo/syscalls/socket.go b/libgo/syscalls/socket.go index 28581a5..be7a89b 100644 --- a/libgo/syscalls/socket.go +++ b/libgo/syscalls/socket.go @@ -131,7 +131,7 @@ func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, int) { } return sa, 0; } - return nil, EAFNOSUPPORT; + return anyToSockaddrOS(rsa) } func libc_accept(fd int, sa *RawSockaddrAny, len *Socklen_t) int __asm__ ("accept"); @@ -239,7 +239,7 @@ func SetsockoptString(fd, level, opt int, s string) (errno int) { return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&[]byte(s)[0])), Socklen_t(len(s))) } -func SetsockoptIpMreq(fd, level, opt int, mreq *IpMreq) (errno int) { +func SetsockoptIPMreq(fd, level, opt int, mreq *IPMreq) (errno int) { return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(mreq)), unsafe.Sizeof(*mreq)) } diff --git a/libgo/syscalls/socket_bsd.go b/libgo/syscalls/socket_bsd.go index f4d06b4..735baf9 100644 --- a/libgo/syscalls/socket_bsd.go +++ b/libgo/syscalls/socket_bsd.go @@ -72,3 +72,7 @@ type RawSockaddr struct { func BindToDevice(fd int, device string) (errno int) { return ENOSYS } + +func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) { + return nil, EAFNOSUPPORT; +} diff --git a/libgo/syscalls/socket_irix.go b/libgo/syscalls/socket_irix.go index e17f164..6f2aaaf 100644 --- a/libgo/syscalls/socket_irix.go +++ b/libgo/syscalls/socket_irix.go @@ -78,7 +78,7 @@ func BindToDevice(fd int, device string) (errno int) { // This could be enabled with -D_SGI_SOURCE, but conflicts with // -D_XOPEN_SOURCE=500 required for msg_control etc. in struct msghgr, so // simply provide it here. -type IpMreq struct { +type IPMreq struct { Multiaddr [4]byte Interface [4]byte } @@ -123,3 +123,7 @@ const ( EAI_OVERFLOW = 13 EAI_MAX = 14 ) + +func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) { + return nil, EAFNOSUPPORT; +} diff --git a/libgo/syscalls/socket_linux.go b/libgo/syscalls/socket_linux.go index cdcdf4f..57ea7c3 100644 --- a/libgo/syscalls/socket_linux.go +++ b/libgo/syscalls/socket_linux.go @@ -6,9 +6,55 @@ package syscall +import "unsafe" + const SizeofSockaddrInet4 = 16 const SizeofSockaddrInet6 = 28 const SizeofSockaddrUnix = 110 +const SizeofSockaddrLinklayer = 20 +const SizeofSockaddrNetlink = 12 + +type SockaddrLinklayer struct { + Protocol uint16 + Ifindex int + Hatype uint16 + Pkttype uint8 + Halen uint8 + Addr [8]byte + raw RawSockaddrLinklayer +} + +func (sa *SockaddrLinklayer) sockaddr() (*RawSockaddrAny, Socklen_t, int) { + if sa.Ifindex < 0 || sa.Ifindex > 0x7fffffff { + return nil, 0, EINVAL + } + sa.raw.Family = AF_PACKET + sa.raw.Protocol = sa.Protocol + sa.raw.Ifindex = int32(sa.Ifindex) + sa.raw.Hatype = sa.Hatype + sa.raw.Pkttype = sa.Pkttype + sa.raw.Halen = sa.Halen + for i := 0; i < len(sa.Addr); i++ { + sa.raw.Addr[i] = sa.Addr[i] + } + return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), SizeofSockaddrLinklayer, 0 +} + +type SockaddrNetlink struct { + Family uint16 + Pad uint16 + Pid uint32 + Groups uint32 + raw RawSockaddrNetlink +} + +func (sa *SockaddrNetlink) sockaddr() (*RawSockaddrAny, Socklen_t, int) { + sa.raw.Family = AF_NETLINK + sa.raw.Pad = sa.Pad + sa.raw.Pid = sa.Pid + sa.raw.Groups = sa.Groups + return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), SizeofSockaddrNetlink, 0 +} type RawSockaddrInet4 struct { Family uint16; @@ -64,6 +110,23 @@ func (sa *RawSockaddrUnix) getLen() (int, int) { return n, 0 } +type RawSockaddrLinklayer struct { + Family uint16 + Protocol uint16 + Ifindex int32 + Hatype uint16 + Pkttype uint8 + Halen uint8 + Addr [8]uint8 +} + +type RawSockaddrNetlink struct { + Family uint16 + Pad uint16 + Pid uint32 + Groups uint32 +} + type RawSockaddr struct { Family uint16; Data [14]int8; @@ -73,3 +136,30 @@ type RawSockaddr struct { func BindToDevice(fd int, device string) (errno int) { return SetsockoptString(fd, SOL_SOCKET, SO_BINDTODEVICE, device) } + +func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) { + switch rsa.Addr.Family { + case AF_NETLINK: + pp := (*RawSockaddrNetlink)(unsafe.Pointer(rsa)) + sa := new(SockaddrNetlink) + sa.Family = pp.Family + sa.Pad = pp.Pad + sa.Pid = pp.Pid + sa.Groups = pp.Groups + return sa, 0 + + case AF_PACKET: + pp := (*RawSockaddrLinklayer)(unsafe.Pointer(rsa)) + sa := new(SockaddrLinklayer) + sa.Protocol = pp.Protocol + sa.Ifindex = int(pp.Ifindex) + sa.Hatype = pp.Hatype + sa.Pkttype = pp.Pkttype + sa.Halen = pp.Halen + for i := 0; i < len(sa.Addr); i++ { + sa.Addr[i] = pp.Addr[i] + } + return sa, 0 + } + return nil, EAFNOSUPPORT; +} diff --git a/libgo/syscalls/socket_solaris.go b/libgo/syscalls/socket_solaris.go index 13fe727..3767078 100644 --- a/libgo/syscalls/socket_solaris.go +++ b/libgo/syscalls/socket_solaris.go @@ -74,3 +74,7 @@ type RawSockaddr struct { func BindToDevice(fd int, device string) (errno int) { return ENOSYS } + +func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) { + return nil, EAFNOSUPPORT; +} diff --git a/libgo/syscalls/syscall_linux.go b/libgo/syscalls/syscall_linux.go index bdb92c5..29c8b62 100644 --- a/libgo/syscalls/syscall_linux.go +++ b/libgo/syscalls/syscall_linux.go @@ -9,6 +9,7 @@ package syscall import "unsafe" func libc_ptrace(request int, pid Pid_t, addr uintptr, data *byte) _C_long __asm__ ("ptrace") +func libc_sendfile(int, int, *Offset_t, Size_t) Ssize_t __asm__ ("sendfile") var dummy *byte const sizeofPtr uintptr = uintptr(unsafe.Sizeof(dummy)) @@ -186,3 +187,20 @@ func Tgkill(tgid int, tid int, sig int) (errno int) { uintptr(sig)); return int(err); } + +func Sendfile(outfd int, infd int, offset *int64, count int) (written int, errno int) { + var o Offset_t + var po *Offset_t + if offset != nil { + o = Offset_t(*offset) + po = &o + } + w := libc_sendfile(outfd, infd, po, Size_t(count)) + if offset != nil { + *offset = int64(o) + } + if w < 0 { + return 0, GetErrno() + } + return int(w), 0 +} |