提交 60d0859b 编写于 作者: Y YiLin.Li

shim/runtime: Add the attestation package to process remote attestation requests

Signed-off-by: NYilin Li <YiLin.Li@linux.alibaba.com>
上级 62f0bb3c
......@@ -17,10 +17,11 @@ require (
github.com/gogo/googleapis v1.4.0 // indirect
github.com/gogo/protobuf v1.3.1
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/golang/protobuf v1.3.5
github.com/imdario/mergo v0.3.9 // indirect
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/opencontainers/runc v0.1.1 // indirect
github.com/opencontainers/runc v0.1.1
github.com/opencontainers/runtime-spec v1.0.2
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.5.0
......@@ -29,10 +30,12 @@ require (
github.com/stretchr/testify v1.4.0
go.etcd.io/bbolt v1.3.4 // indirect
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d
google.golang.org/grpc v1.28.0 // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
k8s.io/apimachinery v0.18.2
)
replace github.com/docker/distribution => github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible
replace (
github.com/docker/distribution => github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible
github.com/opencontainers/runc => github.com/alibaba/inclavare-containers/rune v0.0.0-20200903043353-e35cb5b583ad
)
此差异已折叠。
......@@ -4,5 +4,10 @@ const (
ConfigurationPath = "/etc/inclavare-containers/config.toml"
RuneOCIRuntime = "rune"
EnvKeyRuneCarrier = "RUNE_CARRIER"
EnvKeyRaType = "ENCLAVE_IS_RA_TYPE_EPID"
EnvKeyIsProductEnclave = "ENCLAVE_IS_PRODUCT_ENCLAVE"
EnvKeyRaEpidSpid = "ENCLAVE_RA_EPID_SPID"
EnvKeyRaEpidSubKey = "ENCLAVE_RA_EPID_SUB_KEY"
EnvKeyRaEpidIsLinkable = "ENCLAVE_RA_EPID_IS_LINKABLE"
RuneDefaultWorkDirectory = "/run/rune"
)
package attestation
import (
"context"
"fmt"
"github.com/alibaba/inclavare-containers/shim/runtime/config"
"github.com/alibaba/inclavare-containers/shim/runtime/v2/rune/constants"
"github.com/opencontainers/runc/libenclave/attestation/sgx"
pb "github.com/opencontainers/runc/libenclave/proto"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
"net"
"path"
"path/filepath"
"strings"
)
const (
agentSocket = "agent.sock"
)
const (
QuoteSignatureTypeUnlinkable = iota
QuoteSignatureTypeLinkable
InvalidQuoteSignatureType
)
func dialAgentSocket(root string, containerId string) (*net.UnixConn, error) {
agentSock := filepath.Join(root, containerId, agentSocket)
addr, err := net.ResolveUnixAddr("unix", agentSock)
if err != nil {
return nil, err
}
conn, err := net.DialUnix("unix", nil, addr)
if err != nil {
return nil, err
}
return conn, nil
}
func GetRaParameters(bundlePath string) (raParameters map[string]string, err error) {
configPath := path.Join(bundlePath, "config.json")
p := make(map[string]string)
var spec *specs.Spec
spec, err = config.LoadSpec(configPath)
if err != nil {
return nil, fmt.Errorf("Load Spec:%s error:%s", configPath, err)
}
v, ok := config.GetEnv(spec, constants.EnvKeyRaType)
if !ok {
logrus.Infof("remote attestation parameters aren't set")
return nil, nil
}
p[constants.EnvKeyRaType] = v
v, ok = config.GetEnv(spec, constants.EnvKeyIsProductEnclave)
if !ok {
return nil, fmt.Errorf("Env:%s isn't set", constants.EnvKeyIsProductEnclave)
}
p[constants.EnvKeyIsProductEnclave] = v
v, ok = config.GetEnv(spec, constants.EnvKeyRaEpidSpid)
if !ok {
return nil, fmt.Errorf("Env:%s isn't set", constants.EnvKeyRaEpidSpid)
}
p[constants.EnvKeyRaEpidSpid] = v
v, ok = config.GetEnv(spec, constants.EnvKeyRaEpidSubKey)
if !ok {
return nil, fmt.Errorf("Env:%s isn't set", constants.EnvKeyRaEpidSubKey)
}
p[constants.EnvKeyRaEpidSubKey] = v
v, ok = config.GetEnv(spec, constants.EnvKeyRaEpidIsLinkable)
if !ok {
return nil, fmt.Errorf("Env:%s isn't set", constants.EnvKeyRaEpidIsLinkable)
}
p[constants.EnvKeyRaEpidIsLinkable] = v
return p, nil
}
func Attest(ctx context.Context, raParameters map[string]string, containerId string, root string) (map[string]string, error) {
if raParameters == nil {
return nil, nil
}
if raParameters[constants.EnvKeyRaType] == "" {
return nil, nil
}
if !strings.EqualFold(raParameters[constants.EnvKeyRaType], "true") {
return nil, fmt.Errorf("Unsupported ra type:%s!\n", raParameters[constants.EnvKeyRaType])
}
/* spid and subscriptionKey is checked in
* package github.com/opencontainers/runc/libenclave/attestation/sgx/ias.
* so we only need to check containerId, product and linkable here.
*/
if containerId == "" {
return nil, fmt.Errorf("Invalid container ID!\n")
}
if root == "" {
return nil, fmt.Errorf("Invalid rune global options --root")
}
conn, err := dialAgentSocket(root, containerId)
if err != nil {
return nil, err
}
isProductEnclave := sgx.DebugEnclave
if strings.EqualFold(raParameters[constants.EnvKeyIsProductEnclave], "true") {
isProductEnclave = sgx.ProductEnclave
}
raEpidQuoteType := QuoteSignatureTypeUnlinkable
if strings.EqualFold(raParameters[constants.EnvKeyRaEpidIsLinkable], "true") {
raEpidQuoteType = QuoteSignatureTypeLinkable
}
req := &pb.AgentServiceRequest{}
req.Attest = &pb.AgentServiceRequest_Attest{
Spid: raParameters[constants.EnvKeyRaEpidSpid],
SubscriptionKey: raParameters[constants.EnvKeyRaEpidSubKey],
Product: (uint32)(isProductEnclave),
QuoteType: (uint32)(raEpidQuoteType),
}
if err = protoBufWrite(conn, req); err != nil {
return nil, err
}
logrus.Infof("Begin remote attestation")
resp := &pb.AgentServiceResponse{}
if err = protoBufRead(conn, resp); err != nil {
return nil, err
}
logrus.Infof("End remote attestation")
if resp.Attest.Error != "" {
err = fmt.Errorf(resp.Attest.Error)
return nil, err
}
iasReport := make(map[string]string)
iasReport["StatusCode"] = resp.Attest.StatusCode
iasReport["Request-ID"] = resp.Attest.RequestID
iasReport["X-Iasreport-Signature"] = resp.Attest.XIasreportSignature
iasReport["X-Iasreport-Signing-Certificate"] = resp.Attest.XIasreportSigningCertificate
iasReport["ContentLength"] = resp.Attest.ContentLength
iasReport["Content-Type"] = resp.Attest.ContentType
iasReport["Body"] = resp.Attest.Body
return iasReport, nil
}
package attestation
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/golang/protobuf/proto"
pb "github.com/opencontainers/runc/libenclave/proto"
"io"
"unsafe"
)
func protoBufWrite(conn io.Writer, marshaled interface{}) (err error) {
var data []byte
switch marshaled := marshaled.(type) {
case *pb.AgentServiceRequest:
data, err = proto.Marshal(marshaled)
case *pb.AgentServiceResponse:
data, err = proto.Marshal(marshaled)
default:
return fmt.Errorf("invalid type of marshaled data")
}
if err != nil {
return err
}
sz := uint32(len(data))
buf := bytes.NewBuffer([]byte{})
binary.Write(buf, binary.LittleEndian, &sz)
if _, err := conn.Write(buf.Bytes()); err != nil {
return err
}
if _, err := conn.Write(data); err != nil {
return err
}
return nil
}
func protoBufRead(conn io.Reader, unmarshaled interface{}) error {
var sz uint32
data := make([]byte, unsafe.Sizeof(sz))
_, err := conn.Read(data)
if err != nil {
return err
}
buf := bytes.NewBuffer(data)
sz = uint32(len(data))
if err := binary.Read(buf, binary.LittleEndian, &sz); err != nil {
return err
}
data = make([]byte, sz)
if _, err := conn.Read(data); err != nil {
return err
}
switch unmarshaled := unmarshaled.(type) {
case *pb.AgentServiceRequest:
err = proto.Unmarshal(data, unmarshaled)
case *pb.AgentServiceResponse:
err = proto.Unmarshal(data, unmarshaled)
default:
return fmt.Errorf("invalid type of unmarshaled data")
}
return nil
}
......@@ -58,6 +58,7 @@ type Epoller struct {
efd int
mu sync.Mutex
fdMapping map[int]*EpollConsole
closeOnce sync.Once
}
// NewEpoller returns an instance of epoller with a valid epoll fd.
......@@ -151,7 +152,11 @@ func (e *Epoller) getConsole(sysfd int) *EpollConsole {
// Close closes the epoll fd
func (e *Epoller) Close() error {
return unix.Close(e.efd)
closeErr := os.ErrClosed // default to "file already closed"
e.closeOnce.Do(func() {
closeErr = unix.Close(e.efd)
})
return closeErr
}
// EpollConsole acts like a console but registers its file descriptor with an
......
......@@ -74,6 +74,3 @@ func (m *defaultMonitor) Wait(c *exec.Cmd, ec chan Exit) (int, error) {
e := <-ec
return e.Status, nil
}
The MIT License (MIT)
Copyright (c) 2014 Brian Goff
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
package md2man
import (
"github.com/russross/blackfriday/v2"
)
// Render converts a markdown document into a roff formatted document.
func Render(doc []byte) []byte {
renderer := NewRoffRenderer()
return blackfriday.Run(doc,
[]blackfriday.Option{blackfriday.WithRenderer(renderer),
blackfriday.WithExtensions(renderer.GetExtensions())}...)
}
package md2man
import (
"fmt"
"io"
"os"
"strings"
"github.com/russross/blackfriday/v2"
)
// roffRenderer implements the blackfriday.Renderer interface for creating
// roff format (manpages) from markdown text
type roffRenderer struct {
extensions blackfriday.Extensions
listCounters []int
firstHeader bool
defineTerm bool
listDepth int
}
const (
titleHeader = ".TH "
topLevelHeader = "\n\n.SH "
secondLevelHdr = "\n.SH "
otherHeader = "\n.SS "
crTag = "\n"
emphTag = "\\fI"
emphCloseTag = "\\fP"
strongTag = "\\fB"
strongCloseTag = "\\fP"
breakTag = "\n.br\n"
paraTag = "\n.PP\n"
hruleTag = "\n.ti 0\n\\l'\\n(.lu'\n"
linkTag = "\n\\[la]"
linkCloseTag = "\\[ra]"
codespanTag = "\\fB\\fC"
codespanCloseTag = "\\fR"
codeTag = "\n.PP\n.RS\n\n.nf\n"
codeCloseTag = "\n.fi\n.RE\n"
quoteTag = "\n.PP\n.RS\n"
quoteCloseTag = "\n.RE\n"
listTag = "\n.RS\n"
listCloseTag = "\n.RE\n"
arglistTag = "\n.TP\n"
tableStart = "\n.TS\nallbox;\n"
tableEnd = ".TE\n"
tableCellStart = "T{\n"
tableCellEnd = "\nT}\n"
)
// NewRoffRenderer creates a new blackfriday Renderer for generating roff documents
// from markdown
func NewRoffRenderer() *roffRenderer { // nolint: golint
var extensions blackfriday.Extensions
extensions |= blackfriday.NoIntraEmphasis
extensions |= blackfriday.Tables
extensions |= blackfriday.FencedCode
extensions |= blackfriday.SpaceHeadings
extensions |= blackfriday.Footnotes
extensions |= blackfriday.Titleblock
extensions |= blackfriday.DefinitionLists
return &roffRenderer{
extensions: extensions,
}
}
// GetExtensions returns the list of extensions used by this renderer implementation
func (r *roffRenderer) GetExtensions() blackfriday.Extensions {
return r.extensions
}
// RenderHeader handles outputting the header at document start
func (r *roffRenderer) RenderHeader(w io.Writer, ast *blackfriday.Node) {
// disable hyphenation
out(w, ".nh\n")
}
// RenderFooter handles outputting the footer at the document end; the roff
// renderer has no footer information
func (r *roffRenderer) RenderFooter(w io.Writer, ast *blackfriday.Node) {
}
// RenderNode is called for each node in a markdown document; based on the node
// type the equivalent roff output is sent to the writer
func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
var walkAction = blackfriday.GoToNext
switch node.Type {
case blackfriday.Text:
r.handleText(w, node, entering)
case blackfriday.Softbreak:
out(w, crTag)
case blackfriday.Hardbreak:
out(w, breakTag)
case blackfriday.Emph:
if entering {
out(w, emphTag)
} else {
out(w, emphCloseTag)
}
case blackfriday.Strong:
if entering {
out(w, strongTag)
} else {
out(w, strongCloseTag)
}
case blackfriday.Link:
if !entering {
out(w, linkTag+string(node.LinkData.Destination)+linkCloseTag)
}
case blackfriday.Image:
// ignore images
walkAction = blackfriday.SkipChildren
case blackfriday.Code:
out(w, codespanTag)
escapeSpecialChars(w, node.Literal)
out(w, codespanCloseTag)
case blackfriday.Document:
break
case blackfriday.Paragraph:
// roff .PP markers break lists
if r.listDepth > 0 {
return blackfriday.GoToNext
}
if entering {
out(w, paraTag)
} else {
out(w, crTag)
}
case blackfriday.BlockQuote:
if entering {
out(w, quoteTag)
} else {
out(w, quoteCloseTag)
}
case blackfriday.Heading:
r.handleHeading(w, node, entering)
case blackfriday.HorizontalRule:
out(w, hruleTag)
case blackfriday.List:
r.handleList(w, node, entering)
case blackfriday.Item:
r.handleItem(w, node, entering)
case blackfriday.CodeBlock:
out(w, codeTag)
escapeSpecialChars(w, node.Literal)
out(w, codeCloseTag)
case blackfriday.Table:
r.handleTable(w, node, entering)
case blackfriday.TableCell:
r.handleTableCell(w, node, entering)
case blackfriday.TableHead:
case blackfriday.TableBody:
case blackfriday.TableRow:
// no action as cell entries do all the nroff formatting
return blackfriday.GoToNext
default:
fmt.Fprintln(os.Stderr, "WARNING: go-md2man does not handle node type "+node.Type.String())
}
return walkAction
}
func (r *roffRenderer) handleText(w io.Writer, node *blackfriday.Node, entering bool) {
var (
start, end string
)
// handle special roff table cell text encapsulation
if node.Parent.Type == blackfriday.TableCell {
if len(node.Literal) > 30 {
start = tableCellStart
end = tableCellEnd
} else {
// end rows that aren't terminated by "tableCellEnd" with a cr if end of row
if node.Parent.Next == nil && !node.Parent.IsHeader {
end = crTag
}
}
}
out(w, start)
escapeSpecialChars(w, node.Literal)
out(w, end)
}
func (r *roffRenderer) handleHeading(w io.Writer, node *blackfriday.Node, entering bool) {
if entering {
switch node.Level {
case 1:
if !r.firstHeader {
out(w, titleHeader)
r.firstHeader = true
break
}
out(w, topLevelHeader)
case 2:
out(w, secondLevelHdr)
default:
out(w, otherHeader)
}
}
}
func (r *roffRenderer) handleList(w io.Writer, node *blackfriday.Node, entering bool) {
openTag := listTag
closeTag := listCloseTag
if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
// tags for definition lists handled within Item node
openTag = ""
closeTag = ""
}
if entering {
r.listDepth++
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
r.listCounters = append(r.listCounters, 1)
}
out(w, openTag)
} else {
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
r.listCounters = r.listCounters[:len(r.listCounters)-1]
}
out(w, closeTag)
r.listDepth--
}
}
func (r *roffRenderer) handleItem(w io.Writer, node *blackfriday.Node, entering bool) {
if entering {
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
out(w, fmt.Sprintf(".IP \"%3d.\" 5\n", r.listCounters[len(r.listCounters)-1]))
r.listCounters[len(r.listCounters)-1]++
} else if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
// state machine for handling terms and following definitions
// since blackfriday does not distinguish them properly, nor
// does it seperate them into separate lists as it should
if !r.defineTerm {
out(w, arglistTag)
r.defineTerm = true
} else {
r.defineTerm = false
}
} else {
out(w, ".IP \\(bu 2\n")
}
} else {
out(w, "\n")
}
}
func (r *roffRenderer) handleTable(w io.Writer, node *blackfriday.Node, entering bool) {
if entering {
out(w, tableStart)
//call walker to count cells (and rows?) so format section can be produced
columns := countColumns(node)
out(w, strings.Repeat("l ", columns)+"\n")
out(w, strings.Repeat("l ", columns)+".\n")
} else {
out(w, tableEnd)
}
}
func (r *roffRenderer) handleTableCell(w io.Writer, node *blackfriday.Node, entering bool) {
var (
start, end string
)
if node.IsHeader {
start = codespanTag
end = codespanCloseTag
}
if entering {
if node.Prev != nil && node.Prev.Type == blackfriday.TableCell {
out(w, "\t"+start)
} else {
out(w, start)
}
} else {
// need to carriage return if we are at the end of the header row
if node.IsHeader && node.Next == nil {
end = end + crTag
}
out(w, end)
}
}
// because roff format requires knowing the column count before outputting any table
// data we need to walk a table tree and count the columns
func countColumns(node *blackfriday.Node) int {
var columns int
node.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
switch node.Type {
case blackfriday.TableRow:
if !entering {
return blackfriday.Terminate
}
case blackfriday.TableCell:
if entering {
columns++
}
default:
}
return blackfriday.GoToNext
})
return columns
}
func out(w io.Writer, output string) {
io.WriteString(w, output) // nolint: errcheck
}
func needsBackslash(c byte) bool {
for _, r := range []byte("-_&\\~") {
if c == r {
return true
}
}
return false
}
func escapeSpecialChars(w io.Writer, text []byte) {
for i := 0; i < len(text); i++ {
// escape initial apostrophe or period
if len(text) >= 1 && (text[0] == '\'' || text[0] == '.') {
out(w, "\\&")
}
// directly copy normal characters
org := i
for i < len(text) && !needsBackslash(text[i]) {
i++
}
if i > org {
w.Write(text[org:i]) // nolint: errcheck
}
// escape a character
if i >= len(text) {
break
}
w.Write([]byte{'\\', text[i]}) // nolint: errcheck
}
}
......@@ -102,7 +102,8 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
//
type Any struct {
// A URL/resource name that uniquely identifies the type of the serialized
// protocol buffer message. The last segment of the URL's path must represent
// protocol buffer message. This string must contain at least
// one "/" character. The last segment of the URL's path must represent
// the fully qualified name of the type (as in
// `path/google.protobuf.Duration`). The name should be in a canonical form
// (e.g., leading "." is not accepted).
......@@ -181,7 +182,9 @@ func init() {
proto.RegisterType((*Any)(nil), "google.protobuf.Any")
}
func init() { proto.RegisterFile("google/protobuf/any.proto", fileDescriptor_b53526c13ae22eb4) }
func init() {
proto.RegisterFile("google/protobuf/any.proto", fileDescriptor_b53526c13ae22eb4)
}
var fileDescriptor_b53526c13ae22eb4 = []byte{
// 185 bytes of a gzipped FileDescriptorProto
......
......@@ -121,7 +121,8 @@ option objc_class_prefix = "GPB";
//
message Any {
// A URL/resource name that uniquely identifies the type of the serialized
// protocol buffer message. The last segment of the URL's path must represent
// protocol buffer message. This string must contain at least
// one "/" character. The last segment of the URL's path must represent
// the fully qualified name of the type (as in
// `path/google.protobuf.Duration`). The name should be in a canonical form
// (e.g., leading "." is not accepted).
......
......@@ -41,7 +41,7 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// if (duration.seconds < 0 && duration.nanos > 0) {
// duration.seconds += 1;
// duration.nanos -= 1000000000;
// } else if (durations.seconds > 0 && duration.nanos < 0) {
// } else if (duration.seconds > 0 && duration.nanos < 0) {
// duration.seconds -= 1;
// duration.nanos += 1000000000;
// }
......@@ -142,7 +142,9 @@ func init() {
proto.RegisterType((*Duration)(nil), "google.protobuf.Duration")
}
func init() { proto.RegisterFile("google/protobuf/duration.proto", fileDescriptor_23597b2ebd7ac6c5) }
func init() {
proto.RegisterFile("google/protobuf/duration.proto", fileDescriptor_23597b2ebd7ac6c5)
}
var fileDescriptor_23597b2ebd7ac6c5 = []byte{
// 190 bytes of a gzipped FileDescriptorProto
......
......@@ -61,7 +61,7 @@ option objc_class_prefix = "GPB";
// if (duration.seconds < 0 && duration.nanos > 0) {
// duration.seconds += 1;
// duration.nanos -= 1000000000;
// } else if (durations.seconds > 0 && duration.nanos < 0) {
// } else if (duration.seconds > 0 && duration.nanos < 0) {
// duration.seconds -= 1;
// duration.nanos += 1000000000;
// }
......@@ -101,7 +101,6 @@ option objc_class_prefix = "GPB";
//
//
message Duration {
// Signed seconds of the span of time. Must be from -315,576,000,000
// to +315,576,000,000 inclusive. Note: these bounds are computed from:
// 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
......
......@@ -20,17 +20,19 @@ var _ = math.Inf
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// A Timestamp represents a point in time independent of any time zone
// or calendar, represented as seconds and fractions of seconds at
// nanosecond resolution in UTC Epoch time. It is encoded using the
// Proleptic Gregorian Calendar which extends the Gregorian calendar
// backwards to year one. It is encoded assuming all minutes are 60
// seconds long, i.e. leap seconds are "smeared" so that no leap second
// table is needed for interpretation. Range is from
// 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z.
// By restricting to that range, we ensure that we can convert to
// and from RFC 3339 date strings.
// See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt).
// A Timestamp represents a point in time independent of any time zone or local
// calendar, encoded as a count of seconds and fractions of seconds at
// nanosecond resolution. The count is relative to an epoch at UTC midnight on
// January 1, 1970, in the proleptic Gregorian calendar which extends the
// Gregorian calendar backwards to year one.
//
// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
// second table is needed for interpretation, using a [24-hour linear
// smear](https://developers.google.com/time/smear).
//
// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
// restricting to that range, we ensure that we can convert to and from [RFC
// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
//
// # Examples
//
......@@ -91,12 +93,14 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// 01:30 UTC on January 15, 2017.
//
// In JavaScript, one can convert a Date object to this format using the
// standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString]
// standard
// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
// method. In Python, a standard `datetime.datetime` object can be converted
// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
// can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--
// to this format using
// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
// the Joda Time's [`ISODateTimeFormat.dateTime()`](
// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D
// ) to obtain a formatter capable of generating timestamps in this format.
//
//
......@@ -160,7 +164,9 @@ func init() {
proto.RegisterType((*Timestamp)(nil), "google.protobuf.Timestamp")
}
func init() { proto.RegisterFile("google/protobuf/timestamp.proto", fileDescriptor_292007bbfe81227e) }
func init() {
proto.RegisterFile("google/protobuf/timestamp.proto", fileDescriptor_292007bbfe81227e)
}
var fileDescriptor_292007bbfe81227e = []byte{
// 191 bytes of a gzipped FileDescriptorProto
......
......@@ -40,17 +40,19 @@ option java_outer_classname = "TimestampProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
// A Timestamp represents a point in time independent of any time zone
// or calendar, represented as seconds and fractions of seconds at
// nanosecond resolution in UTC Epoch time. It is encoded using the
// Proleptic Gregorian Calendar which extends the Gregorian calendar
// backwards to year one. It is encoded assuming all minutes are 60
// seconds long, i.e. leap seconds are "smeared" so that no leap second
// table is needed for interpretation. Range is from
// 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z.
// By restricting to that range, we ensure that we can convert to
// and from RFC 3339 date strings.
// See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt).
// A Timestamp represents a point in time independent of any time zone or local
// calendar, encoded as a count of seconds and fractions of seconds at
// nanosecond resolution. The count is relative to an epoch at UTC midnight on
// January 1, 1970, in the proleptic Gregorian calendar which extends the
// Gregorian calendar backwards to year one.
//
// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
// second table is needed for interpretation, using a [24-hour linear
// smear](https://developers.google.com/time/smear).
//
// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
// restricting to that range, we ensure that we can convert to and from [RFC
// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
//
// # Examples
//
......@@ -111,17 +113,18 @@ option objc_class_prefix = "GPB";
// 01:30 UTC on January 15, 2017.
//
// In JavaScript, one can convert a Date object to this format using the
// standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString]
// standard
// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
// method. In Python, a standard `datetime.datetime` object can be converted
// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
// can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--
// to this format using
// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
// the Joda Time's [`ISODateTimeFormat.dateTime()`](
// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D
// ) to obtain a formatter capable of generating timestamps in this format.
//
//
message Timestamp {
// Represents seconds of UTC time since Unix epoch
// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
// 9999-12-31T23:59:59Z inclusive.
......
......@@ -3,26 +3,13 @@
package system
import (
"bufio"
"fmt"
"os"
"os/exec"
"syscall"
"unsafe"
)
// If arg2 is nonzero, set the "child subreaper" attribute of the
// calling process; if arg2 is zero, unset the attribute. When a
// process is marked as a child subreaper, all of the children
// that it creates, and their descendants, will be marked as
// having a subreaper. In effect, a subreaper fulfills the role
// of init(1) for its descendant processes. Upon termination of
// a process that is orphaned (i.e., its immediate parent has
// already terminated) and marked as having a subreaper, the
// nearest still living ancestor subreaper will receive a SIGCHLD
// signal and be able to wait(2) on the process to discover its
// termination status.
const PR_SET_CHILD_SUBREAPER = 36
"github.com/opencontainers/runc/libcontainer/user"
"golang.org/x/sys/unix"
)
type ParentDeathSignal int
......@@ -50,11 +37,11 @@ func Execv(cmd string, args []string, env []string) error {
return err
}
return syscall.Exec(name, args, env)
return unix.Exec(name, args, env)
}
func Prlimit(pid, resource int, limit syscall.Rlimit) error {
_, _, err := syscall.RawSyscall6(syscall.SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(&limit)), uintptr(unsafe.Pointer(&limit)), 0, 0)
func Prlimit(pid, resource int, limit unix.Rlimit) error {
_, _, err := unix.RawSyscall6(unix.SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(&limit)), uintptr(unsafe.Pointer(&limit)), 0, 0)
if err != 0 {
return err
}
......@@ -62,7 +49,7 @@ func Prlimit(pid, resource int, limit syscall.Rlimit) error {
}
func SetParentDeathSignal(sig uintptr) error {
if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, sig, 0); err != 0 {
if err := unix.Prctl(unix.PR_SET_PDEATHSIG, sig, 0, 0, 0); err != nil {
return err
}
return nil
......@@ -70,15 +57,14 @@ func SetParentDeathSignal(sig uintptr) error {
func GetParentDeathSignal() (ParentDeathSignal, error) {
var sig int
_, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_GET_PDEATHSIG, uintptr(unsafe.Pointer(&sig)), 0)
if err != 0 {
if err := unix.Prctl(unix.PR_GET_PDEATHSIG, uintptr(unsafe.Pointer(&sig)), 0, 0, 0); err != nil {
return -1, err
}
return ParentDeathSignal(sig), nil
}
func SetKeepCaps() error {
if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_KEEPCAPS, 1, 0); err != 0 {
if err := unix.Prctl(unix.PR_SET_KEEPCAPS, 1, 0, 0, 0); err != nil {
return err
}
......@@ -86,7 +72,7 @@ func SetKeepCaps() error {
}
func ClearKeepCaps() error {
if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_KEEPCAPS, 0, 0); err != 0 {
if err := unix.Prctl(unix.PR_SET_KEEPCAPS, 0, 0, 0, 0); err != nil {
return err
}
......@@ -94,55 +80,62 @@ func ClearKeepCaps() error {
}
func Setctty() error {
if _, _, err := syscall.RawSyscall(syscall.SYS_IOCTL, 0, uintptr(syscall.TIOCSCTTY), 0); err != 0 {
if err := unix.IoctlSetInt(0, unix.TIOCSCTTY, 0); err != nil {
return err
}
return nil
}
/*
* Detect whether we are currently running in a user namespace.
* Copied from github.com/lxc/lxd/shared/util.go
*/
// RunningInUserNS detects whether we are currently running in a user namespace.
// Originally copied from github.com/lxc/lxd/shared/util.go
func RunningInUserNS() bool {
file, err := os.Open("/proc/self/uid_map")
if err != nil {
/*
* This kernel-provided file only exists if user namespaces are
* supported
*/
return false
}
defer file.Close()
buf := bufio.NewReader(file)
l, _, err := buf.ReadLine()
uidmap, err := user.CurrentProcessUIDMap()
if err != nil {
// This kernel-provided file only exists if user namespaces are supported
return false
}
return UIDMapInUserNS(uidmap)
}
line := string(l)
var a, b, c int64
fmt.Sscanf(line, "%d %d %d", &a, &b, &c)
func UIDMapInUserNS(uidmap []user.IDMap) bool {
/*
* We assume we are in the initial user namespace if we have a full
* range - 4294967295 uids starting at uid 0.
*/
if a == 0 && b == 0 && c == 4294967295 {
if len(uidmap) == 1 && uidmap[0].ID == 0 && uidmap[0].ParentID == 0 && uidmap[0].Count == 4294967295 {
return false
}
return true
}
// GetParentNSeuid returns the euid within the parent user namespace
func GetParentNSeuid() int64 {
euid := int64(os.Geteuid())
uidmap, err := user.CurrentProcessUIDMap()
if err != nil {
// This kernel-provided file only exists if user namespaces are supported
return euid
}
for _, um := range uidmap {
if um.ID <= euid && euid <= um.ID+um.Count-1 {
return um.ParentID + euid - um.ID
}
}
return euid
}
// SetSubreaper sets the value i as the subreaper setting for the calling process
func SetSubreaper(i int) error {
return Prctl(PR_SET_CHILD_SUBREAPER, uintptr(i), 0, 0, 0)
return unix.Prctl(unix.PR_SET_CHILD_SUBREAPER, uintptr(i), 0, 0, 0)
}
func Prctl(option int, arg2, arg3, arg4, arg5 uintptr) (err error) {
_, _, e1 := syscall.Syscall6(syscall.SYS_PRCTL, uintptr(option), arg2, arg3, arg4, arg5, 0)
if e1 != 0 {
err = e1
// GetSubreaper returns the subreaper setting for the calling process
func GetSubreaper() (int, error) {
var i uintptr
if err := unix.Prctl(unix.PR_GET_CHILD_SUBREAPER, uintptr(unsafe.Pointer(&i)), 0, 0, 0); err != nil {
return -1, err
}
return
return int(i), nil
}
package system
import (
"fmt"
"io/ioutil"
"path/filepath"
"strconv"
"strings"
)
// look in /proc to find the process start time so that we can verify
// that this pid has started after ourself
// State is the status of a process.
type State rune
const ( // Only values for Linux 3.14 and later are listed here
Dead State = 'X'
DiskSleep State = 'D'
Running State = 'R'
Sleeping State = 'S'
Stopped State = 'T'
TracingStop State = 't'
Zombie State = 'Z'
)
// String forms of the state from proc(5)'s documentation for
// /proc/[pid]/status' "State" field.
func (s State) String() string {
switch s {
case Dead:
return "dead"
case DiskSleep:
return "disk sleep"
case Running:
return "running"
case Sleeping:
return "sleeping"
case Stopped:
return "stopped"
case TracingStop:
return "tracing stop"
case Zombie:
return "zombie"
default:
return fmt.Sprintf("unknown (%c)", s)
}
}
// Stat_t represents the information from /proc/[pid]/stat, as
// described in proc(5) with names based on the /proc/[pid]/status
// fields.
type Stat_t struct {
// PID is the process ID.
PID uint
// Name is the command run by the process.
Name string
// State is the state of the process.
State State
// StartTime is the number of clock ticks after system boot (since
// Linux 2.6).
StartTime uint64
}
// Stat returns a Stat_t instance for the specified process.
func Stat(pid int) (stat Stat_t, err error) {
bytes, err := ioutil.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "stat"))
if err != nil {
return stat, err
}
return parseStat(string(bytes))
}
// GetProcessStartTime is deprecated. Use Stat(pid) and
// Stat_t.StartTime instead.
func GetProcessStartTime(pid int) (string, error) {
data, err := ioutil.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "stat"))
stat, err := Stat(pid)
if err != nil {
return "", err
}
return fmt.Sprintf("%d", stat.StartTime), nil
}
func parseStat(data string) (stat Stat_t, err error) {
// From proc(5), field 2 could contain space and is inside `(` and `)`.
// The following is an example:
// 89653 (gunicorn: maste) S 89630 89653 89653 0 -1 4194560 29689 28896 0 3 146 32 76 19 20 0 1 0 2971844 52965376 3920 18446744073709551615 1 1 0 0 0 0 0 16781312 137447943 0 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0
i := strings.LastIndex(data, ")")
if i <= 2 || i >= len(data)-1 {
return stat, fmt.Errorf("invalid stat data: %q", data)
}
parts := strings.SplitN(data[:i], "(", 2)
if len(parts) != 2 {
return stat, fmt.Errorf("invalid stat data: %q", data)
}
stat.Name = parts[1]
_, err = fmt.Sscanf(parts[0], "%d", &stat.PID)
if err != nil {
return stat, err
}
parts := strings.Split(string(data), " ")
// the starttime is located at pos 22
// from the man page
//
// starttime %llu (was %lu before Linux 2.6)
// (22) The time the process started after system boot. In kernels before Linux 2.6, this
// value was expressed in jiffies. Since Linux 2.6, the value is expressed in clock ticks
// (divide by sysconf(_SC_CLK_TCK)).
return parts[22-1], nil // starts at 1
// parts indexes should be offset by 3 from the field number given
// proc(5), because parts is zero-indexed and we've removed fields
// one (PID) and two (Name) in the paren-split.
parts = strings.Split(data[i+2:], " ")
var state int
fmt.Sscanf(parts[3-3], "%c", &state)
stat.State = State(state)
fmt.Sscanf(parts[22-3], "%d", &stat.StartTime)
return stat, nil
}
package system
import (
"fmt"
"runtime"
"syscall"
)
// Via http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=7b21fddd087678a70ad64afc0f632e0f1071b092
//
// We need different setns values for the different platforms and arch
// We are declaring the macro here because the SETNS syscall does not exist in th stdlib
var setNsMap = map[string]uintptr{
"linux/386": 346,
"linux/arm64": 268,
"linux/amd64": 308,
"linux/arm": 375,
"linux/ppc": 350,
"linux/ppc64": 350,
"linux/ppc64le": 350,
"linux/s390x": 339,
}
var sysSetns = setNsMap[fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)]
func SysSetns() uint32 {
return uint32(sysSetns)
}
func Setns(fd uintptr, flags uintptr) error {
ns, exists := setNsMap[fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)]
if !exists {
return fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime.GOARCH)
}
_, _, err := syscall.RawSyscall(ns, fd, flags, 0)
if err != 0 {
return err
}
return nil
}
// +build linux,386
// +build linux
// +build 386 arm
package system
import (
"syscall"
"golang.org/x/sys/unix"
)
// Setuid sets the uid of the calling thread to the specified uid.
func Setuid(uid int) (err error) {
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETUID, uintptr(uid), 0, 0)
_, _, e1 := unix.RawSyscall(unix.SYS_SETUID32, uintptr(uid), 0, 0)
if e1 != 0 {
err = e1
}
......@@ -17,7 +18,7 @@ func Setuid(uid int) (err error) {
// Setgid sets the gid of the calling thread to the specified gid.
func Setgid(gid int) (err error) {
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETGID32, uintptr(gid), 0, 0)
_, _, e1 := unix.RawSyscall(unix.SYS_SETGID32, uintptr(gid), 0, 0)
if e1 != 0 {
err = e1
}
......
// +build linux,arm64 linux,amd64 linux,ppc linux,ppc64 linux,ppc64le linux,s390x
// +build linux
// +build arm64 amd64 mips mipsle mips64 mips64le ppc ppc64 ppc64le riscv64 s390x
package system
import (
"syscall"
"golang.org/x/sys/unix"
)
// Setuid sets the uid of the calling thread to the specified uid.
func Setuid(uid int) (err error) {
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETUID, uintptr(uid), 0, 0)
_, _, e1 := unix.RawSyscall(unix.SYS_SETUID, uintptr(uid), 0, 0)
if e1 != 0 {
err = e1
}
......@@ -17,7 +18,7 @@ func Setuid(uid int) (err error) {
// Setgid sets the gid of the calling thread to the specified gid.
func Setgid(gid int) (err error) {
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETGID, uintptr(gid), 0, 0)
_, _, e1 := unix.RawSyscall(unix.SYS_SETGID, uintptr(gid), 0, 0)
if e1 != 0 {
err = e1
}
......
// +build linux,arm
package system
import (
"syscall"
)
// Setuid sets the uid of the calling thread to the specified uid.
func Setuid(uid int) (err error) {
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETUID32, uintptr(uid), 0, 0)
if e1 != 0 {
err = e1
}
return
}
// Setgid sets the gid of the calling thread to the specified gid.
func Setgid(gid int) (err error) {
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETGID32, uintptr(gid), 0, 0)
if e1 != 0 {
err = e1
}
return
}
// +build cgo,linux cgo,freebsd
// +build cgo,linux
package system
......
......@@ -2,8 +2,26 @@
package system
import (
"os"
"github.com/opencontainers/runc/libcontainer/user"
)
// RunningInUserNS is a stub for non-Linux systems
// Always returns false
func RunningInUserNS() bool {
return false
}
// UIDMapInUserNS is a stub for non-Linux systems
// Always returns false
func UIDMapInUserNS(uidmap []user.IDMap) bool {
return false
}
// GetParentNSeuid returns the euid within the parent user namespace
// Always returns os.Geteuid on non-linux
func GetParentNSeuid() int {
return os.Geteuid()
}
package system
import (
"syscall"
"unsafe"
)
var _zero uintptr
// Returns the size of xattrs and nil error
// Requires path, takes allocated []byte or nil as last argument
func Llistxattr(path string, dest []byte) (size int, err error) {
pathBytes, err := syscall.BytePtrFromString(path)
if err != nil {
return -1, err
}
var newpathBytes unsafe.Pointer
if len(dest) > 0 {
newpathBytes = unsafe.Pointer(&dest[0])
} else {
newpathBytes = unsafe.Pointer(&_zero)
}
_size, _, errno := syscall.Syscall6(syscall.SYS_LLISTXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(newpathBytes), uintptr(len(dest)), 0, 0, 0)
size = int(_size)
if errno != 0 {
return -1, errno
}
return size, nil
}
import "golang.org/x/sys/unix"
// Returns a []byte slice if the xattr is set and nil otherwise
// Requires path and its attribute as arguments
func Lgetxattr(path string, attr string) ([]byte, error) {
var sz int
pathBytes, err := syscall.BytePtrFromString(path)
if err != nil {
return nil, err
}
attrBytes, err := syscall.BytePtrFromString(attr)
if err != nil {
return nil, err
}
// Start with a 128 length byte array
sz = 128
dest := make([]byte, sz)
destBytes := unsafe.Pointer(&dest[0])
_sz, _, errno := syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0)
dest := make([]byte, 128)
sz, errno := unix.Lgetxattr(path, attr, dest)
switch {
case errno == syscall.ENODATA:
case errno == unix.ENODATA:
return nil, errno
case errno == syscall.ENOTSUP:
case errno == unix.ENOTSUP:
return nil, errno
case errno == syscall.ERANGE:
case errno == unix.ERANGE:
// 128 byte array might just not be good enough,
// A dummy buffer is used ``uintptr(0)`` to get real size
// A dummy buffer is used to get the real size
// of the xattrs on disk
_sz, _, errno = syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(unsafe.Pointer(nil)), uintptr(0), 0, 0)
sz = int(_sz)
if sz < 0 {
sz, errno = unix.Lgetxattr(path, attr, []byte{})
if errno != nil {
return nil, errno
}
dest = make([]byte, sz)
destBytes := unsafe.Pointer(&dest[0])
_sz, _, errno = syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0)
if errno != 0 {
sz, errno = unix.Lgetxattr(path, attr, dest)
if errno != nil {
return nil, errno
}
case errno != 0:
case errno != nil:
return nil, errno
}
sz = int(_sz)
return dest[:sz], nil
}
func Lsetxattr(path string, attr string, data []byte, flags int) error {
pathBytes, err := syscall.BytePtrFromString(path)
if err != nil {
return err
}
attrBytes, err := syscall.BytePtrFromString(attr)
if err != nil {
return err
}
var dataBytes unsafe.Pointer
if len(data) > 0 {
dataBytes = unsafe.Pointer(&data[0])
} else {
dataBytes = unsafe.Pointer(&_zero)
}
_, _, errno := syscall.Syscall6(syscall.SYS_LSETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(dataBytes), uintptr(len(data)), uintptr(flags), 0)
if errno != 0 {
return errno
}
return nil
}
......@@ -2,7 +2,6 @@ package user
import (
"errors"
"syscall"
)
var (
......@@ -13,98 +12,30 @@ var (
ErrNoGroupEntries = errors.New("no matching entries in group file")
)
func lookupUser(filter func(u User) bool) (User, error) {
// Get operating system-specific passwd reader-closer.
passwd, err := GetPasswd()
if err != nil {
return User{}, err
}
defer passwd.Close()
// Get the users.
users, err := ParsePasswdFilter(passwd, filter)
if err != nil {
return User{}, err
}
// No user entries found.
if len(users) == 0 {
return User{}, ErrNoPasswdEntries
}
// Assume the first entry is the "correct" one.
return users[0], nil
}
// CurrentUser looks up the current user by their user id in /etc/passwd. If the
// user cannot be found (or there is no /etc/passwd file on the filesystem),
// then CurrentUser returns an error.
func CurrentUser() (User, error) {
return LookupUid(syscall.Getuid())
}
// LookupUser looks up a user by their username in /etc/passwd. If the user
// cannot be found (or there is no /etc/passwd file on the filesystem), then
// LookupUser returns an error.
func LookupUser(username string) (User, error) {
return lookupUser(func(u User) bool {
return u.Name == username
})
return lookupUser(username)
}
// LookupUid looks up a user by their user id in /etc/passwd. If the user cannot
// be found (or there is no /etc/passwd file on the filesystem), then LookupId
// returns an error.
func LookupUid(uid int) (User, error) {
return lookupUser(func(u User) bool {
return u.Uid == uid
})
}
func lookupGroup(filter func(g Group) bool) (Group, error) {
// Get operating system-specific group reader-closer.
group, err := GetGroup()
if err != nil {
return Group{}, err
}
defer group.Close()
// Get the users.
groups, err := ParseGroupFilter(group, filter)
if err != nil {
return Group{}, err
}
// No user entries found.
if len(groups) == 0 {
return Group{}, ErrNoGroupEntries
}
// Assume the first entry is the "correct" one.
return groups[0], nil
}
// CurrentGroup looks up the current user's group by their primary group id's
// entry in /etc/passwd. If the group cannot be found (or there is no
// /etc/group file on the filesystem), then CurrentGroup returns an error.
func CurrentGroup() (Group, error) {
return LookupGid(syscall.Getgid())
return lookupUid(uid)
}
// LookupGroup looks up a group by its name in /etc/group. If the group cannot
// be found (or there is no /etc/group file on the filesystem), then LookupGroup
// returns an error.
func LookupGroup(groupname string) (Group, error) {
return lookupGroup(func(g Group) bool {
return g.Name == groupname
})
return lookupGroup(groupname)
}
// LookupGid looks up a group by its group id in /etc/group. If the group cannot
// be found (or there is no /etc/group file on the filesystem), then LookupGid
// returns an error.
func LookupGid(gid int) (Group, error) {
return lookupGroup(func(g Group) bool {
return g.Gid == gid
})
return lookupGid(gid)
}
......@@ -5,6 +5,9 @@ package user
import (
"io"
"os"
"strconv"
"golang.org/x/sys/unix"
)
// Unix-specific path to the passwd and group formatted files.
......@@ -13,6 +16,76 @@ const (
unixGroupPath = "/etc/group"
)
func lookupUser(username string) (User, error) {
return lookupUserFunc(func(u User) bool {
return u.Name == username
})
}
func lookupUid(uid int) (User, error) {
return lookupUserFunc(func(u User) bool {
return u.Uid == uid
})
}
func lookupUserFunc(filter func(u User) bool) (User, error) {
// Get operating system-specific passwd reader-closer.
passwd, err := GetPasswd()
if err != nil {
return User{}, err
}
defer passwd.Close()
// Get the users.
users, err := ParsePasswdFilter(passwd, filter)
if err != nil {
return User{}, err
}
// No user entries found.
if len(users) == 0 {
return User{}, ErrNoPasswdEntries
}
// Assume the first entry is the "correct" one.
return users[0], nil
}
func lookupGroup(groupname string) (Group, error) {
return lookupGroupFunc(func(g Group) bool {
return g.Name == groupname
})
}
func lookupGid(gid int) (Group, error) {
return lookupGroupFunc(func(g Group) bool {
return g.Gid == gid
})
}
func lookupGroupFunc(filter func(g Group) bool) (Group, error) {
// Get operating system-specific group reader-closer.
group, err := GetGroup()
if err != nil {
return Group{}, err
}
defer group.Close()
// Get the users.
groups, err := ParseGroupFilter(group, filter)
if err != nil {
return Group{}, err
}
// No user entries found.
if len(groups) == 0 {
return Group{}, ErrNoGroupEntries
}
// Assume the first entry is the "correct" one.
return groups[0], nil
}
func GetPasswdPath() (string, error) {
return unixPasswdPath, nil
}
......@@ -28,3 +101,44 @@ func GetGroupPath() (string, error) {
func GetGroup() (io.ReadCloser, error) {
return os.Open(unixGroupPath)
}
// CurrentUser looks up the current user by their user id in /etc/passwd. If the
// user cannot be found (or there is no /etc/passwd file on the filesystem),
// then CurrentUser returns an error.
func CurrentUser() (User, error) {
return LookupUid(unix.Getuid())
}
// CurrentGroup looks up the current user's group by their primary group id's
// entry in /etc/passwd. If the group cannot be found (or there is no
// /etc/group file on the filesystem), then CurrentGroup returns an error.
func CurrentGroup() (Group, error) {
return LookupGid(unix.Getgid())
}
func currentUserSubIDs(fileName string) ([]SubID, error) {
u, err := CurrentUser()
if err != nil {
return nil, err
}
filter := func(entry SubID) bool {
return entry.Name == u.Name || entry.Name == strconv.Itoa(u.Uid)
}
return ParseSubIDFileFilter(fileName, filter)
}
func CurrentUserSubUIDs() ([]SubID, error) {
return currentUserSubIDs("/etc/subuid")
}
func CurrentUserSubGIDs() ([]SubID, error) {
return currentUserSubIDs("/etc/subgid")
}
func CurrentProcessUIDMap() ([]IDMap, error) {
return ParseIDMapFile("/proc/self/uid_map")
}
func CurrentProcessGIDMap() ([]IDMap, error) {
return ParseIDMapFile("/proc/self/gid_map")
}
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
package user
import "io"
func GetPasswdPath() (string, error) {
return "", ErrUnsupported
}
func GetPasswd() (io.ReadCloser, error) {
return nil, ErrUnsupported
}
func GetGroupPath() (string, error) {
return "", ErrUnsupported
}
func GetGroup() (io.ReadCloser, error) {
return nil, ErrUnsupported
}
// +build windows
package user
import (
"fmt"
"os/user"
)
func lookupUser(username string) (User, error) {
u, err := user.Lookup(username)
if err != nil {
return User{}, err
}
return userFromOS(u)
}
func lookupUid(uid int) (User, error) {
u, err := user.LookupId(fmt.Sprintf("%d", uid))
if err != nil {
return User{}, err
}
return userFromOS(u)
}
func lookupGroup(groupname string) (Group, error) {
g, err := user.LookupGroup(groupname)
if err != nil {
return Group{}, err
}
return groupFromOS(g)
}
func lookupGid(gid int) (Group, error) {
g, err := user.LookupGroupId(fmt.Sprintf("%d", gid))
if err != nil {
return Group{}, err
}
return groupFromOS(g)
}
......@@ -5,6 +5,7 @@ import (
"fmt"
"io"
"os"
"os/user"
"strconv"
"strings"
)
......@@ -28,6 +29,28 @@ type User struct {
Shell string
}
// userFromOS converts an os/user.(*User) to local User
//
// (This does not include Pass, Shell or Gecos)
func userFromOS(u *user.User) (User, error) {
newUser := User{
Name: u.Username,
Home: u.HomeDir,
}
id, err := strconv.Atoi(u.Uid)
if err != nil {
return newUser, err
}
newUser.Uid = id
id, err = strconv.Atoi(u.Gid)
if err != nil {
return newUser, err
}
newUser.Gid = id
return newUser, nil
}
type Group struct {
Name string
Pass string
......@@ -35,12 +58,46 @@ type Group struct {
List []string
}
// groupFromOS converts an os/user.(*Group) to local Group
//
// (This does not include Pass, Shell or Gecos)
func groupFromOS(g *user.Group) (Group, error) {
newGroup := Group{
Name: g.Name,
}
id, err := strconv.Atoi(g.Gid)
if err != nil {
return newGroup, err
}
newGroup.Gid = id
return newGroup, nil
}
// SubID represents an entry in /etc/sub{u,g}id
type SubID struct {
Name string
SubID int64
Count int64
}
// IDMap represents an entry in /proc/PID/{u,g}id_map
type IDMap struct {
ID int64
ParentID int64
Count int64
}
func parseLine(line string, v ...interface{}) {
if line == "" {
parseParts(strings.Split(line, ":"), v...)
}
func parseParts(parts []string, v ...interface{}) {
if len(parts) == 0 {
return
}
parts := strings.Split(line, ":")
for i, p := range parts {
// Ignore cases where we don't have enough fields to populate the arguments.
// Some configuration files like to misbehave.
......@@ -56,6 +113,8 @@ func parseLine(line string, v ...interface{}) {
case *int:
// "numbers", with conversion errors ignored because of some misbehaving configuration files.
*e, _ = strconv.Atoi(p)
case *int64:
*e, _ = strconv.ParseInt(p, 10, 64)
case *[]string:
// Comma-separated lists.
if p != "" {
......@@ -65,7 +124,7 @@ func parseLine(line string, v ...interface{}) {
}
default:
// Someone goof'd when writing code using this function. Scream so they can hear us.
panic(fmt.Sprintf("parseLine only accepts {*string, *int, *[]string} as arguments! %#v is not a pointer!", e))
panic(fmt.Sprintf("parseLine only accepts {*string, *int, *int64, *[]string} as arguments! %#v is not a pointer!", e))
}
}
}
......@@ -103,10 +162,6 @@ func ParsePasswdFilter(r io.Reader, filter func(User) bool) ([]User, error) {
)
for s.Scan() {
if err := s.Err(); err != nil {
return nil, err
}
line := strings.TrimSpace(s.Text())
if line == "" {
continue
......@@ -124,6 +179,9 @@ func ParsePasswdFilter(r io.Reader, filter func(User) bool) ([]User, error) {
out = append(out, p)
}
}
if err := s.Err(); err != nil {
return nil, err
}
return out, nil
}
......@@ -162,10 +220,6 @@ func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) {
)
for s.Scan() {
if err := s.Err(); err != nil {
return nil, err
}
text := s.Text()
if text == "" {
continue
......@@ -183,6 +237,9 @@ func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) {
out = append(out, p)
}
}
if err := s.Err(); err != nil {
return nil, err
}
return out, nil
}
......@@ -199,18 +256,16 @@ type ExecUser struct {
// files cannot be opened for any reason, the error is ignored and a nil
// io.Reader is passed instead.
func GetExecUserPath(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) {
passwd, err := os.Open(passwdPath)
if err != nil {
passwd = nil
} else {
defer passwd.Close()
var passwd, group io.Reader
if passwdFile, err := os.Open(passwdPath); err == nil {
passwd = passwdFile
defer passwdFile.Close()
}
group, err := os.Open(groupPath)
if err != nil {
group = nil
} else {
defer group.Close()
if groupFile, err := os.Open(groupPath); err == nil {
group = groupFile
defer groupFile.Close()
}
return GetExecUser(userSpec, defaults, passwd, group)
......@@ -343,7 +398,7 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
if len(groups) > 0 {
// First match wins, even if there's more than one matching entry.
user.Gid = groups[0].Gid
} else if groupArg != "" {
} else {
// If we can't find a group with the given name, the only other valid
// option is if it's a numeric group name with no associated entry in group.
......@@ -433,9 +488,117 @@ func GetAdditionalGroups(additionalGroups []string, group io.Reader) ([]int, err
// that opens the groupPath given and gives it as an argument to
// GetAdditionalGroups.
func GetAdditionalGroupsPath(additionalGroups []string, groupPath string) ([]int, error) {
group, err := os.Open(groupPath)
if err == nil {
defer group.Close()
var group io.Reader
if groupFile, err := os.Open(groupPath); err == nil {
group = groupFile
defer groupFile.Close()
}
return GetAdditionalGroups(additionalGroups, group)
}
func ParseSubIDFile(path string) ([]SubID, error) {
subid, err := os.Open(path)
if err != nil {
return nil, err
}
defer subid.Close()
return ParseSubID(subid)
}
func ParseSubID(subid io.Reader) ([]SubID, error) {
return ParseSubIDFilter(subid, nil)
}
func ParseSubIDFileFilter(path string, filter func(SubID) bool) ([]SubID, error) {
subid, err := os.Open(path)
if err != nil {
return nil, err
}
defer subid.Close()
return ParseSubIDFilter(subid, filter)
}
func ParseSubIDFilter(r io.Reader, filter func(SubID) bool) ([]SubID, error) {
if r == nil {
return nil, fmt.Errorf("nil source for subid-formatted data")
}
var (
s = bufio.NewScanner(r)
out = []SubID{}
)
for s.Scan() {
line := strings.TrimSpace(s.Text())
if line == "" {
continue
}
// see: man 5 subuid
p := SubID{}
parseLine(line, &p.Name, &p.SubID, &p.Count)
if filter == nil || filter(p) {
out = append(out, p)
}
}
if err := s.Err(); err != nil {
return nil, err
}
return out, nil
}
func ParseIDMapFile(path string) ([]IDMap, error) {
r, err := os.Open(path)
if err != nil {
return nil, err
}
defer r.Close()
return ParseIDMap(r)
}
func ParseIDMap(r io.Reader) ([]IDMap, error) {
return ParseIDMapFilter(r, nil)
}
func ParseIDMapFileFilter(path string, filter func(IDMap) bool) ([]IDMap, error) {
r, err := os.Open(path)
if err != nil {
return nil, err
}
defer r.Close()
return ParseIDMapFilter(r, filter)
}
func ParseIDMapFilter(r io.Reader, filter func(IDMap) bool) ([]IDMap, error) {
if r == nil {
return nil, fmt.Errorf("nil source for idmap-formatted data")
}
var (
s = bufio.NewScanner(r)
out = []IDMap{}
)
for s.Scan() {
line := strings.TrimSpace(s.Text())
if line == "" {
continue
}
// see: man 7 user_namespaces
p := IDMap{}
parseParts(strings.Fields(line), &p.ID, &p.ParentID, &p.Count)
if filter == nil || filter(p) {
out = append(out, p)
}
}
if err := s.Err(); err != nil {
return nil, err
}
return out, nil
}
package sgx // import "github.com/opencontainers/runc/libenclave/attestation/sgx"
// RA Type
const (
UnknownRaType = iota
EPID
DCAP
)
// RA Enclave Type
const (
InvalidEnclaveType = iota
DebugEnclave
ProductEnclave
)
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: agent-service.proto
package libenclave_proto
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type AgentServiceRequest struct {
Exec *AgentServiceRequest_Execute `protobuf:"bytes,1,opt,name=exec,proto3" json:"exec,omitempty"`
Kill *AgentServiceRequest_Kill `protobuf:"bytes,2,opt,name=kill,proto3" json:"kill,omitempty"`
Attest *AgentServiceRequest_Attest `protobuf:"bytes,3,opt,name=attest,proto3" json:"attest,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AgentServiceRequest) Reset() { *m = AgentServiceRequest{} }
func (m *AgentServiceRequest) String() string { return proto.CompactTextString(m) }
func (*AgentServiceRequest) ProtoMessage() {}
func (*AgentServiceRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_145c985d4df28d67, []int{0}
}
func (m *AgentServiceRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AgentServiceRequest.Unmarshal(m, b)
}
func (m *AgentServiceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AgentServiceRequest.Marshal(b, m, deterministic)
}
func (m *AgentServiceRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AgentServiceRequest.Merge(m, src)
}
func (m *AgentServiceRequest) XXX_Size() int {
return xxx_messageInfo_AgentServiceRequest.Size(m)
}
func (m *AgentServiceRequest) XXX_DiscardUnknown() {
xxx_messageInfo_AgentServiceRequest.DiscardUnknown(m)
}
var xxx_messageInfo_AgentServiceRequest proto.InternalMessageInfo
func (m *AgentServiceRequest) GetExec() *AgentServiceRequest_Execute {
if m != nil {
return m.Exec
}
return nil
}
func (m *AgentServiceRequest) GetKill() *AgentServiceRequest_Kill {
if m != nil {
return m.Kill
}
return nil
}
func (m *AgentServiceRequest) GetAttest() *AgentServiceRequest_Attest {
if m != nil {
return m.Attest
}
return nil
}
type AgentServiceRequest_Execute struct {
Argv string `protobuf:"bytes,1,opt,name=argv,proto3" json:"argv,omitempty"`
Envp string `protobuf:"bytes,2,opt,name=envp,proto3" json:"envp,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AgentServiceRequest_Execute) Reset() { *m = AgentServiceRequest_Execute{} }
func (m *AgentServiceRequest_Execute) String() string { return proto.CompactTextString(m) }
func (*AgentServiceRequest_Execute) ProtoMessage() {}
func (*AgentServiceRequest_Execute) Descriptor() ([]byte, []int) {
return fileDescriptor_145c985d4df28d67, []int{0, 0}
}
func (m *AgentServiceRequest_Execute) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AgentServiceRequest_Execute.Unmarshal(m, b)
}
func (m *AgentServiceRequest_Execute) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AgentServiceRequest_Execute.Marshal(b, m, deterministic)
}
func (m *AgentServiceRequest_Execute) XXX_Merge(src proto.Message) {
xxx_messageInfo_AgentServiceRequest_Execute.Merge(m, src)
}
func (m *AgentServiceRequest_Execute) XXX_Size() int {
return xxx_messageInfo_AgentServiceRequest_Execute.Size(m)
}
func (m *AgentServiceRequest_Execute) XXX_DiscardUnknown() {
xxx_messageInfo_AgentServiceRequest_Execute.DiscardUnknown(m)
}
var xxx_messageInfo_AgentServiceRequest_Execute proto.InternalMessageInfo
func (m *AgentServiceRequest_Execute) GetArgv() string {
if m != nil {
return m.Argv
}
return ""
}
func (m *AgentServiceRequest_Execute) GetEnvp() string {
if m != nil {
return m.Envp
}
return ""
}
type AgentServiceRequest_Kill struct {
Sig int32 `protobuf:"varint,1,opt,name=sig,proto3" json:"sig,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AgentServiceRequest_Kill) Reset() { *m = AgentServiceRequest_Kill{} }
func (m *AgentServiceRequest_Kill) String() string { return proto.CompactTextString(m) }
func (*AgentServiceRequest_Kill) ProtoMessage() {}
func (*AgentServiceRequest_Kill) Descriptor() ([]byte, []int) {
return fileDescriptor_145c985d4df28d67, []int{0, 1}
}
func (m *AgentServiceRequest_Kill) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AgentServiceRequest_Kill.Unmarshal(m, b)
}
func (m *AgentServiceRequest_Kill) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AgentServiceRequest_Kill.Marshal(b, m, deterministic)
}
func (m *AgentServiceRequest_Kill) XXX_Merge(src proto.Message) {
xxx_messageInfo_AgentServiceRequest_Kill.Merge(m, src)
}
func (m *AgentServiceRequest_Kill) XXX_Size() int {
return xxx_messageInfo_AgentServiceRequest_Kill.Size(m)
}
func (m *AgentServiceRequest_Kill) XXX_DiscardUnknown() {
xxx_messageInfo_AgentServiceRequest_Kill.DiscardUnknown(m)
}
var xxx_messageInfo_AgentServiceRequest_Kill proto.InternalMessageInfo
func (m *AgentServiceRequest_Kill) GetSig() int32 {
if m != nil {
return m.Sig
}
return 0
}
type AgentServiceRequest_Attest struct {
Spid string `protobuf:"bytes,1,opt,name=spid,proto3" json:"spid,omitempty"`
SubscriptionKey string `protobuf:"bytes,2,opt,name=subscriptionKey,proto3" json:"subscriptionKey,omitempty"`
Product uint32 `protobuf:"varint,3,opt,name=product,proto3" json:"product,omitempty"`
QuoteType uint32 `protobuf:"varint,4,opt,name=quoteType,proto3" json:"quoteType,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AgentServiceRequest_Attest) Reset() { *m = AgentServiceRequest_Attest{} }
func (m *AgentServiceRequest_Attest) String() string { return proto.CompactTextString(m) }
func (*AgentServiceRequest_Attest) ProtoMessage() {}
func (*AgentServiceRequest_Attest) Descriptor() ([]byte, []int) {
return fileDescriptor_145c985d4df28d67, []int{0, 2}
}
func (m *AgentServiceRequest_Attest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AgentServiceRequest_Attest.Unmarshal(m, b)
}
func (m *AgentServiceRequest_Attest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AgentServiceRequest_Attest.Marshal(b, m, deterministic)
}
func (m *AgentServiceRequest_Attest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AgentServiceRequest_Attest.Merge(m, src)
}
func (m *AgentServiceRequest_Attest) XXX_Size() int {
return xxx_messageInfo_AgentServiceRequest_Attest.Size(m)
}
func (m *AgentServiceRequest_Attest) XXX_DiscardUnknown() {
xxx_messageInfo_AgentServiceRequest_Attest.DiscardUnknown(m)
}
var xxx_messageInfo_AgentServiceRequest_Attest proto.InternalMessageInfo
func (m *AgentServiceRequest_Attest) GetSpid() string {
if m != nil {
return m.Spid
}
return ""
}
func (m *AgentServiceRequest_Attest) GetSubscriptionKey() string {
if m != nil {
return m.SubscriptionKey
}
return ""
}
func (m *AgentServiceRequest_Attest) GetProduct() uint32 {
if m != nil {
return m.Product
}
return 0
}
func (m *AgentServiceRequest_Attest) GetQuoteType() uint32 {
if m != nil {
return m.QuoteType
}
return 0
}
type AgentServiceResponse struct {
Exec *AgentServiceResponse_Execute `protobuf:"bytes,1,opt,name=exec,proto3" json:"exec,omitempty"`
Attest *AgentServiceResponse_Attest `protobuf:"bytes,2,opt,name=attest,proto3" json:"attest,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AgentServiceResponse) Reset() { *m = AgentServiceResponse{} }
func (m *AgentServiceResponse) String() string { return proto.CompactTextString(m) }
func (*AgentServiceResponse) ProtoMessage() {}
func (*AgentServiceResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_145c985d4df28d67, []int{1}
}
func (m *AgentServiceResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AgentServiceResponse.Unmarshal(m, b)
}
func (m *AgentServiceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AgentServiceResponse.Marshal(b, m, deterministic)
}
func (m *AgentServiceResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_AgentServiceResponse.Merge(m, src)
}
func (m *AgentServiceResponse) XXX_Size() int {
return xxx_messageInfo_AgentServiceResponse.Size(m)
}
func (m *AgentServiceResponse) XXX_DiscardUnknown() {
xxx_messageInfo_AgentServiceResponse.DiscardUnknown(m)
}
var xxx_messageInfo_AgentServiceResponse proto.InternalMessageInfo
func (m *AgentServiceResponse) GetExec() *AgentServiceResponse_Execute {
if m != nil {
return m.Exec
}
return nil
}
func (m *AgentServiceResponse) GetAttest() *AgentServiceResponse_Attest {
if m != nil {
return m.Attest
}
return nil
}
type AgentServiceResponse_Execute struct {
ExitCode int32 `protobuf:"varint,1,opt,name=exitCode,proto3" json:"exitCode,omitempty"`
Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AgentServiceResponse_Execute) Reset() { *m = AgentServiceResponse_Execute{} }
func (m *AgentServiceResponse_Execute) String() string { return proto.CompactTextString(m) }
func (*AgentServiceResponse_Execute) ProtoMessage() {}
func (*AgentServiceResponse_Execute) Descriptor() ([]byte, []int) {
return fileDescriptor_145c985d4df28d67, []int{1, 0}
}
func (m *AgentServiceResponse_Execute) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AgentServiceResponse_Execute.Unmarshal(m, b)
}
func (m *AgentServiceResponse_Execute) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AgentServiceResponse_Execute.Marshal(b, m, deterministic)
}
func (m *AgentServiceResponse_Execute) XXX_Merge(src proto.Message) {
xxx_messageInfo_AgentServiceResponse_Execute.Merge(m, src)
}
func (m *AgentServiceResponse_Execute) XXX_Size() int {
return xxx_messageInfo_AgentServiceResponse_Execute.Size(m)
}
func (m *AgentServiceResponse_Execute) XXX_DiscardUnknown() {
xxx_messageInfo_AgentServiceResponse_Execute.DiscardUnknown(m)
}
var xxx_messageInfo_AgentServiceResponse_Execute proto.InternalMessageInfo
func (m *AgentServiceResponse_Execute) GetExitCode() int32 {
if m != nil {
return m.ExitCode
}
return 0
}
func (m *AgentServiceResponse_Execute) GetError() string {
if m != nil {
return m.Error
}
return ""
}
type AgentServiceResponse_Attest struct {
ExitCode int32 `protobuf:"varint,1,opt,name=exitCode,proto3" json:"exitCode,omitempty"`
Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"`
StatusCode string `protobuf:"bytes,3,opt,name=statusCode,proto3" json:"statusCode,omitempty"`
RequestID string `protobuf:"bytes,4,opt,name=requestID,proto3" json:"requestID,omitempty"`
XIasreportSignature string `protobuf:"bytes,5,opt,name=xIasreportSignature,proto3" json:"xIasreportSignature,omitempty"`
XIasreportSigningCertificate string `protobuf:"bytes,6,opt,name=xIasreportSigningCertificate,proto3" json:"xIasreportSigningCertificate,omitempty"`
ContentLength string `protobuf:"bytes,7,opt,name=contentLength,proto3" json:"contentLength,omitempty"`
ContentType string `protobuf:"bytes,8,opt,name=contentType,proto3" json:"contentType,omitempty"`
Body string `protobuf:"bytes,9,opt,name=body,proto3" json:"body,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AgentServiceResponse_Attest) Reset() { *m = AgentServiceResponse_Attest{} }
func (m *AgentServiceResponse_Attest) String() string { return proto.CompactTextString(m) }
func (*AgentServiceResponse_Attest) ProtoMessage() {}
func (*AgentServiceResponse_Attest) Descriptor() ([]byte, []int) {
return fileDescriptor_145c985d4df28d67, []int{1, 1}
}
func (m *AgentServiceResponse_Attest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AgentServiceResponse_Attest.Unmarshal(m, b)
}
func (m *AgentServiceResponse_Attest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AgentServiceResponse_Attest.Marshal(b, m, deterministic)
}
func (m *AgentServiceResponse_Attest) XXX_Merge(src proto.Message) {
xxx_messageInfo_AgentServiceResponse_Attest.Merge(m, src)
}
func (m *AgentServiceResponse_Attest) XXX_Size() int {
return xxx_messageInfo_AgentServiceResponse_Attest.Size(m)
}
func (m *AgentServiceResponse_Attest) XXX_DiscardUnknown() {
xxx_messageInfo_AgentServiceResponse_Attest.DiscardUnknown(m)
}
var xxx_messageInfo_AgentServiceResponse_Attest proto.InternalMessageInfo
func (m *AgentServiceResponse_Attest) GetExitCode() int32 {
if m != nil {
return m.ExitCode
}
return 0
}
func (m *AgentServiceResponse_Attest) GetError() string {
if m != nil {
return m.Error
}
return ""
}
func (m *AgentServiceResponse_Attest) GetStatusCode() string {
if m != nil {
return m.StatusCode
}
return ""
}
func (m *AgentServiceResponse_Attest) GetRequestID() string {
if m != nil {
return m.RequestID
}
return ""
}
func (m *AgentServiceResponse_Attest) GetXIasreportSignature() string {
if m != nil {
return m.XIasreportSignature
}
return ""
}
func (m *AgentServiceResponse_Attest) GetXIasreportSigningCertificate() string {
if m != nil {
return m.XIasreportSigningCertificate
}
return ""
}
func (m *AgentServiceResponse_Attest) GetContentLength() string {
if m != nil {
return m.ContentLength
}
return ""
}
func (m *AgentServiceResponse_Attest) GetContentType() string {
if m != nil {
return m.ContentType
}
return ""
}
func (m *AgentServiceResponse_Attest) GetBody() string {
if m != nil {
return m.Body
}
return ""
}
func init() {
proto.RegisterType((*AgentServiceRequest)(nil), "libenclave_proto.AgentServiceRequest")
proto.RegisterType((*AgentServiceRequest_Execute)(nil), "libenclave_proto.AgentServiceRequest.Execute")
proto.RegisterType((*AgentServiceRequest_Kill)(nil), "libenclave_proto.AgentServiceRequest.Kill")
proto.RegisterType((*AgentServiceRequest_Attest)(nil), "libenclave_proto.AgentServiceRequest.Attest")
proto.RegisterType((*AgentServiceResponse)(nil), "libenclave_proto.AgentServiceResponse")
proto.RegisterType((*AgentServiceResponse_Execute)(nil), "libenclave_proto.AgentServiceResponse.Execute")
proto.RegisterType((*AgentServiceResponse_Attest)(nil), "libenclave_proto.AgentServiceResponse.Attest")
}
func init() {
proto.RegisterFile("agent-service.proto", fileDescriptor_145c985d4df28d67)
}
var fileDescriptor_145c985d4df28d67 = []byte{
// 455 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0xcf, 0x6a, 0xdb, 0x40,
0x10, 0xc6, 0x89, 0x2d, 0xdb, 0xd1, 0x84, 0xd0, 0xb0, 0xce, 0x61, 0x11, 0xa1, 0x84, 0xd0, 0x43,
0x28, 0x8d, 0xe8, 0x9f, 0x63, 0xa1, 0xe0, 0xfc, 0x39, 0x84, 0xf4, 0xb4, 0xe9, 0xbd, 0xc8, 0xf2,
0x54, 0x5d, 0x2a, 0x76, 0x95, 0xdd, 0x91, 0xb1, 0x2f, 0x7d, 0xa1, 0xbc, 0x49, 0x9f, 0xaa, 0xec,
0x48, 0xb1, 0x63, 0x61, 0x5a, 0xe7, 0x36, 0xfb, 0xed, 0xfc, 0x3e, 0x76, 0xbf, 0x19, 0x18, 0x67,
0x05, 0x1a, 0xba, 0xf0, 0xe8, 0xe6, 0x3a, 0xc7, 0xb4, 0x72, 0x96, 0xac, 0x38, 0x2a, 0xf5, 0x14,
0x4d, 0x5e, 0x66, 0x73, 0xfc, 0xce, 0xca, 0xd9, 0x63, 0x1f, 0xc6, 0x93, 0xd0, 0x79, 0xdf, 0x34,
0x2a, 0x7c, 0xa8, 0xd1, 0x93, 0x98, 0x40, 0x84, 0x0b, 0xcc, 0xe5, 0xde, 0xe9, 0xde, 0xf9, 0xc1,
0xc7, 0x8b, 0xb4, 0x0b, 0xa6, 0x5b, 0xa0, 0xf4, 0x66, 0x81, 0x79, 0x4d, 0xa8, 0x18, 0x15, 0x5f,
0x20, 0xfa, 0xa5, 0xcb, 0x52, 0xf6, 0xd8, 0xe2, 0xed, 0x6e, 0x16, 0x77, 0xba, 0x2c, 0x15, 0x73,
0xe2, 0x1a, 0x86, 0x19, 0x11, 0x7a, 0x92, 0x7d, 0x76, 0x78, 0xb7, 0x9b, 0xc3, 0x84, 0x19, 0xd5,
0xb2, 0xc9, 0x07, 0x18, 0xb5, 0xcf, 0x12, 0x02, 0xa2, 0xcc, 0x15, 0x73, 0xfe, 0x53, 0xac, 0xb8,
0x0e, 0x1a, 0x9a, 0x79, 0xc5, 0x8f, 0x8c, 0x15, 0xd7, 0x89, 0x84, 0x28, 0x3c, 0x43, 0x1c, 0x41,
0xdf, 0xeb, 0x82, 0xdb, 0x07, 0x2a, 0x94, 0xc9, 0x6f, 0x18, 0x36, 0xf6, 0x81, 0xf3, 0x95, 0x9e,
0x3d, 0x79, 0x85, 0x5a, 0x9c, 0xc3, 0x2b, 0x5f, 0x4f, 0x7d, 0xee, 0x74, 0x45, 0xda, 0x9a, 0x3b,
0x5c, 0xb6, 0xb6, 0x5d, 0x59, 0x48, 0x18, 0x55, 0xce, 0xce, 0xea, 0xbc, 0xf9, 0xdb, 0xa1, 0x7a,
0x3a, 0x8a, 0x13, 0x88, 0x1f, 0x6a, 0x4b, 0xf8, 0x6d, 0x59, 0xa1, 0x8c, 0xf8, 0x6e, 0x2d, 0x9c,
0x3d, 0x46, 0x70, 0xbc, 0xf9, 0x67, 0x5f, 0x59, 0xe3, 0x51, 0x5c, 0x6e, 0x8c, 0x2b, 0xfd, 0x5f,
0x52, 0x0d, 0xd5, 0x99, 0xd7, 0xcd, 0x2a, 0xef, 0xde, 0x6e, 0x43, 0x6f, 0x5d, 0x3a, 0x81, 0x7f,
0x5e, 0x07, 0x9e, 0xc0, 0x3e, 0x2e, 0x34, 0x5d, 0xd9, 0x19, 0xb6, 0x29, 0xae, 0xce, 0xe2, 0x18,
0x06, 0xe8, 0x9c, 0x75, 0x6d, 0x44, 0xcd, 0x21, 0xf9, 0xd3, 0x5b, 0x25, 0xfc, 0x62, 0x58, 0xbc,
0x06, 0xf0, 0x94, 0x51, 0xed, 0x99, 0xe9, 0xf3, 0xd5, 0x33, 0x25, 0x64, 0xeb, 0x9a, 0x25, 0xb9,
0xbd, 0xe6, 0x6c, 0x63, 0xb5, 0x16, 0xc4, 0x7b, 0x18, 0x2f, 0x6e, 0x33, 0xef, 0xb0, 0xb2, 0x8e,
0xee, 0x75, 0x61, 0x32, 0xaa, 0x1d, 0xca, 0x01, 0xf7, 0x6d, 0xbb, 0x12, 0x97, 0x70, 0xb2, 0x29,
0x6b, 0x53, 0x5c, 0xa1, 0x23, 0xfd, 0x43, 0xe7, 0x19, 0xa1, 0x1c, 0x32, 0xfa, 0xcf, 0x1e, 0xf1,
0x06, 0x0e, 0x73, 0x6b, 0x08, 0x0d, 0x7d, 0x45, 0x53, 0xd0, 0x4f, 0x39, 0x62, 0x68, 0x53, 0x14,
0xa7, 0x70, 0xd0, 0x0a, 0xbc, 0x17, 0xfb, 0xdc, 0xf3, 0x5c, 0x0a, 0xfb, 0x38, 0xb5, 0xb3, 0xa5,
0x8c, 0x9b, 0x7d, 0x0c, 0xf5, 0x74, 0xc8, 0x43, 0xfb, 0xf4, 0x37, 0x00, 0x00, 0xff, 0xff, 0xd5,
0xb7, 0xb8, 0x57, 0x0b, 0x04, 0x00, 0x00,
}
syntax = "proto3";
package libenclave_proto;
message AgentServiceRequest{
message Execute {
string argv = 1;
string envp = 2;
}
message Kill {
int32 sig = 1;
}
message Attest {
string spid = 1;
string subscriptionKey = 2;
uint32 product = 3;
uint32 quoteType = 4;
}
Execute exec = 1;
Kill kill = 2;
Attest attest = 3;
}
message AgentServiceResponse {
message Execute {
int32 exitCode = 1;
string error = 2;
}
message Attest {
int32 exitCode = 1;
string error = 2;
string statusCode = 3;
string requestID = 4;
string xIasreportSignature = 5;
string xIasreportSigningCertificate = 6;
string contentLength = 7;
string contentType = 8;
string body = 9;
}
Execute exec = 1;
Attest attest = 2;
}
syntax = "proto2";
package libenclave_proto;
message AgentServiceRequest{
message Execute {
required string argv = 1;
required string envp = 2;
}
message Kill {
required int32 sig = 1;
}
optional Execute exec = 1;
optional Kill kill = 2;
}
message AgentServiceResponse {
message Execute {
required int32 exitCode = 1;
required string error = 2;
}
optional Execute exec = 1;
}
*.out
*.swp
*.8
*.6
_obj
_test*
markdown
tags
sudo: false
language: go
go:
- "1.10.x"
- "1.11.x"
- tip
matrix:
fast_finish: true
allow_failures:
- go: tip
install:
- # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step).
script:
- go get -t -v ./...
- diff -u <(echo -n) <(gofmt -d -s .)
- go tool vet .
- go test -v ./...
Blackfriday is distributed under the Simplified BSD License:
> Copyright © 2011 Russ Ross
> All rights reserved.
>
> Redistribution and use in source and binary forms, with or without
> modification, are permitted provided that the following conditions
> are met:
>
> 1. Redistributions of source code must retain the above copyright
> notice, this list of conditions and the following disclaimer.
>
> 2. Redistributions in binary form must reproduce the above
> copyright notice, this list of conditions and the following
> disclaimer in the documentation and/or other materials provided with
> the distribution.
>
> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
> FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
> BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
> LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
> CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
> LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
> ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
> POSSIBILITY OF SUCH DAMAGE.
Blackfriday [![Build Status](https://travis-ci.org/russross/blackfriday.svg?branch=master)](https://travis-ci.org/russross/blackfriday)
===========
Blackfriday is a [Markdown][1] processor implemented in [Go][2]. It
is paranoid about its input (so you can safely feed it user-supplied
data), it is fast, it supports common extensions (tables, smart
punctuation substitutions, etc.), and it is safe for all utf-8
(unicode) input.
HTML output is currently supported, along with Smartypants
extensions.
It started as a translation from C of [Sundown][3].
Installation
------------
Blackfriday is compatible with any modern Go release. With Go 1.7 and git
installed:
go get gopkg.in/russross/blackfriday.v2
will download, compile, and install the package into your `$GOPATH`
directory hierarchy. Alternatively, you can achieve the same if you
import it into a project:
import "gopkg.in/russross/blackfriday.v2"
and `go get` without parameters.
Versions
--------
Currently maintained and recommended version of Blackfriday is `v2`. It's being
developed on its own branch: https://github.com/russross/blackfriday/tree/v2 and the
documentation is available at
https://godoc.org/gopkg.in/russross/blackfriday.v2.
It is `go get`-able via via [gopkg.in][6] at `gopkg.in/russross/blackfriday.v2`,
but we highly recommend using package management tool like [dep][7] or
[Glide][8] and make use of semantic versioning. With package management you
should import `github.com/russross/blackfriday` and specify that you're using
version 2.0.0.
Version 2 offers a number of improvements over v1:
* Cleaned up API
* A separate call to [`Parse`][4], which produces an abstract syntax tree for
the document
* Latest bug fixes
* Flexibility to easily add your own rendering extensions
Potential drawbacks:
* Our benchmarks show v2 to be slightly slower than v1. Currently in the
ballpark of around 15%.
* API breakage. If you can't afford modifying your code to adhere to the new API
and don't care too much about the new features, v2 is probably not for you.
* Several bug fixes are trailing behind and still need to be forward-ported to
v2. See issue [#348](https://github.com/russross/blackfriday/issues/348) for
tracking.
Usage
-----
For the most sensible markdown processing, it is as simple as getting your input
into a byte slice and calling:
```go
output := blackfriday.Run(input)
```
Your input will be parsed and the output rendered with a set of most popular
extensions enabled. If you want the most basic feature set, corresponding with
the bare Markdown specification, use:
```go
output := blackfriday.Run(input, blackfriday.WithNoExtensions())
```
### Sanitize untrusted content
Blackfriday itself does nothing to protect against malicious content. If you are
dealing with user-supplied markdown, we recommend running Blackfriday's output
through HTML sanitizer such as [Bluemonday][5].
Here's an example of simple usage of Blackfriday together with Bluemonday:
```go
import (
"github.com/microcosm-cc/bluemonday"
"github.com/russross/blackfriday"
)
// ...
unsafe := blackfriday.Run(input)
html := bluemonday.UGCPolicy().SanitizeBytes(unsafe)
```
### Custom options
If you want to customize the set of options, use `blackfriday.WithExtensions`,
`blackfriday.WithRenderer` and `blackfriday.WithRefOverride`.
You can also check out `blackfriday-tool` for a more complete example
of how to use it. Download and install it using:
go get github.com/russross/blackfriday-tool
This is a simple command-line tool that allows you to process a
markdown file using a standalone program. You can also browse the
source directly on github if you are just looking for some example
code:
* <http://github.com/russross/blackfriday-tool>
Note that if you have not already done so, installing
`blackfriday-tool` will be sufficient to download and install
blackfriday in addition to the tool itself. The tool binary will be
installed in `$GOPATH/bin`. This is a statically-linked binary that
can be copied to wherever you need it without worrying about
dependencies and library versions.
Features
--------
All features of Sundown are supported, including:
* **Compatibility**. The Markdown v1.0.3 test suite passes with
the `--tidy` option. Without `--tidy`, the differences are
mostly in whitespace and entity escaping, where blackfriday is
more consistent and cleaner.
* **Common extensions**, including table support, fenced code
blocks, autolinks, strikethroughs, non-strict emphasis, etc.
* **Safety**. Blackfriday is paranoid when parsing, making it safe
to feed untrusted user input without fear of bad things
happening. The test suite stress tests this and there are no
known inputs that make it crash. If you find one, please let me
know and send me the input that does it.
NOTE: "safety" in this context means *runtime safety only*. In order to
protect yourself against JavaScript injection in untrusted content, see
[this example](https://github.com/russross/blackfriday#sanitize-untrusted-content).
* **Fast processing**. It is fast enough to render on-demand in
most web applications without having to cache the output.
* **Thread safety**. You can run multiple parsers in different
goroutines without ill effect. There is no dependence on global
shared state.
* **Minimal dependencies**. Blackfriday only depends on standard
library packages in Go. The source code is pretty
self-contained, so it is easy to add to any project, including
Google App Engine projects.
* **Standards compliant**. Output successfully validates using the
W3C validation tool for HTML 4.01 and XHTML 1.0 Transitional.
Extensions
----------
In addition to the standard markdown syntax, this package
implements the following extensions:
* **Intra-word emphasis supression**. The `_` character is
commonly used inside words when discussing code, so having
markdown interpret it as an emphasis command is usually the
wrong thing. Blackfriday lets you treat all emphasis markers as
normal characters when they occur inside a word.
* **Tables**. Tables can be created by drawing them in the input
using a simple syntax:
```
Name | Age
--------|------
Bob | 27
Alice | 23
```
* **Fenced code blocks**. In addition to the normal 4-space
indentation to mark code blocks, you can explicitly mark them
and supply a language (to make syntax highlighting simple). Just
mark it like this:
```go
func getTrue() bool {
return true
}
```
You can use 3 or more backticks to mark the beginning of the
block, and the same number to mark the end of the block.
* **Definition lists**. A simple definition list is made of a single-line
term followed by a colon and the definition for that term.
Cat
: Fluffy animal everyone likes
Internet
: Vector of transmission for pictures of cats
Terms must be separated from the previous definition by a blank line.
* **Footnotes**. A marker in the text that will become a superscript number;
a footnote definition that will be placed in a list of footnotes at the
end of the document. A footnote looks like this:
This is a footnote.[^1]
[^1]: the footnote text.
* **Autolinking**. Blackfriday can find URLs that have not been
explicitly marked as links and turn them into links.
* **Strikethrough**. Use two tildes (`~~`) to mark text that
should be crossed out.
* **Hard line breaks**. With this extension enabled newlines in the input
translate into line breaks in the output. This extension is off by default.
* **Smart quotes**. Smartypants-style punctuation substitution is
supported, turning normal double- and single-quote marks into
curly quotes, etc.
* **LaTeX-style dash parsing** is an additional option, where `--`
is translated into `&ndash;`, and `---` is translated into
`&mdash;`. This differs from most smartypants processors, which
turn a single hyphen into an ndash and a double hyphen into an
mdash.
* **Smart fractions**, where anything that looks like a fraction
is translated into suitable HTML (instead of just a few special
cases like most smartypant processors). For example, `4/5`
becomes `<sup>4</sup>&frasl;<sub>5</sub>`, which renders as
<sup>4</sup>&frasl;<sub>5</sub>.
Other renderers
---------------
Blackfriday is structured to allow alternative rendering engines. Here
are a few of note:
* [github_flavored_markdown](https://godoc.org/github.com/shurcooL/github_flavored_markdown):
provides a GitHub Flavored Markdown renderer with fenced code block
highlighting, clickable heading anchor links.
It's not customizable, and its goal is to produce HTML output
equivalent to the [GitHub Markdown API endpoint](https://developer.github.com/v3/markdown/#render-a-markdown-document-in-raw-mode),
except the rendering is performed locally.
* [markdownfmt](https://github.com/shurcooL/markdownfmt): like gofmt,
but for markdown.
* [LaTeX output](https://github.com/Ambrevar/Blackfriday-LaTeX):
renders output as LaTeX.
* [Blackfriday-Confluence](https://github.com/kentaro-m/blackfriday-confluence): provides a [Confluence Wiki Markup](https://confluence.atlassian.com/doc/confluence-wiki-markup-251003035.html) renderer.
Todo
----
* More unit testing
* Improve unicode support. It does not understand all unicode
rules (about what constitutes a letter, a punctuation symbol,
etc.), so it may fail to detect word boundaries correctly in
some instances. It is safe on all utf-8 input.
License
-------
[Blackfriday is distributed under the Simplified BSD License](LICENSE.txt)
[1]: https://daringfireball.net/projects/markdown/ "Markdown"
[2]: https://golang.org/ "Go Language"
[3]: https://github.com/vmg/sundown "Sundown"
[4]: https://godoc.org/gopkg.in/russross/blackfriday.v2#Parse "Parse func"
[5]: https://github.com/microcosm-cc/bluemonday "Bluemonday"
[6]: https://labix.org/gopkg.in "gopkg.in"
此差异已折叠。
// Package blackfriday is a markdown processor.
//
// It translates plain text with simple formatting rules into an AST, which can
// then be further processed to HTML (provided by Blackfriday itself) or other
// formats (provided by the community).
//
// The simplest way to invoke Blackfriday is to call the Run function. It will
// take a text input and produce a text output in HTML (or other format).
//
// A slightly more sophisticated way to use Blackfriday is to create a Markdown
// processor and to call Parse, which returns a syntax tree for the input
// document. You can leverage Blackfriday's parsing for content extraction from
// markdown documents. You can assign a custom renderer and set various options
// to the Markdown processor.
//
// If you're interested in calling Blackfriday from command line, see
// https://github.com/russross/blackfriday-tool.
package blackfriday
package blackfriday
import (
"html"
"io"
)
var htmlEscaper = [256][]byte{
'&': []byte("&amp;"),
'<': []byte("&lt;"),
'>': []byte("&gt;"),
'"': []byte("&quot;"),
}
func escapeHTML(w io.Writer, s []byte) {
var start, end int
for end < len(s) {
escSeq := htmlEscaper[s[end]]
if escSeq != nil {
w.Write(s[start:end])
w.Write(escSeq)
start = end + 1
}
end++
}
if start < len(s) && end <= len(s) {
w.Write(s[start:end])
}
}
func escLink(w io.Writer, text []byte) {
unesc := html.UnescapeString(string(text))
escapeHTML(w, []byte(unesc))
}
module github.com/russross/blackfriday/v2
此差异已折叠。
此差异已折叠。
sudo: false
language: go
go:
- 1.x
- master
matrix:
allow_failures:
- go: master
fast_finish: true
install:
- # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step).
script:
- go get -t -v ./...
- diff -u <(echo -n) <(gofmt -d -s .)
- go tool vet .
- go test -v -race ./...
MIT License
Copyright (c) 2015 Dmitri Shuralyov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
sanitized_anchor_name
=====================
[![Build Status](https://travis-ci.org/shurcooL/sanitized_anchor_name.svg?branch=master)](https://travis-ci.org/shurcooL/sanitized_anchor_name) [![GoDoc](https://godoc.org/github.com/shurcooL/sanitized_anchor_name?status.svg)](https://godoc.org/github.com/shurcooL/sanitized_anchor_name)
Package sanitized_anchor_name provides a func to create sanitized anchor names.
Its logic can be reused by multiple packages to create interoperable anchor names
and links to those anchors.
At this time, it does not try to ensure that generated anchor names
are unique, that responsibility falls on the caller.
Installation
------------
```bash
go get -u github.com/shurcooL/sanitized_anchor_name
```
Example
-------
```Go
anchorName := sanitized_anchor_name.Create("This is a header")
fmt.Println(anchorName)
// Output:
// this-is-a-header
```
License
-------
- [MIT License](LICENSE)
module github.com/shurcooL/sanitized_anchor_name
// Package sanitized_anchor_name provides a func to create sanitized anchor names.
//
// Its logic can be reused by multiple packages to create interoperable anchor names
// and links to those anchors.
//
// At this time, it does not try to ensure that generated anchor names
// are unique, that responsibility falls on the caller.
package sanitized_anchor_name // import "github.com/shurcooL/sanitized_anchor_name"
import "unicode"
// Create returns a sanitized anchor name for the given text.
func Create(text string) string {
var anchorName []rune
var futureDash = false
for _, r := range text {
switch {
case unicode.IsLetter(r) || unicode.IsNumber(r):
if futureDash && len(anchorName) > 0 {
anchorName = append(anchorName, '-')
}
futureDash = false
anchorName = append(anchorName, unicode.ToLower(r))
default:
futureDash = true
}
}
return string(anchorName)
}
......@@ -60,13 +60,74 @@ type Capabilities interface {
Apply(kind CapType) error
}
// NewPid create new initialized Capabilities object for given pid when it
// is nonzero, or for the current pid if pid is 0
// NewPid initializes a new Capabilities object for given pid when
// it is nonzero, or for the current process if pid is 0.
//
// Deprecated: Replace with NewPid2. For example, replace:
//
// c, err := NewPid(0)
// if err != nil {
// return err
// }
//
// with:
//
// c, err := NewPid2(0)
// if err != nil {
// return err
// }
// err = c.Load()
// if err != nil {
// return err
// }
func NewPid(pid int) (Capabilities, error) {
c, err := newPid(pid)
if err != nil {
return c, err
}
err = c.Load()
return c, err
}
// NewPid2 initializes a new Capabilities object for given pid when
// it is nonzero, or for the current process if pid is 0. This
// does not load the process's current capabilities; to do that you
// must call Load explicitly.
func NewPid2(pid int) (Capabilities, error) {
return newPid(pid)
}
// NewFile create new initialized Capabilities object for given named file.
func NewFile(name string) (Capabilities, error) {
return newFile(name)
// NewFile initializes a new Capabilities object for given file path.
//
// Deprecated: Replace with NewFile2. For example, replace:
//
// c, err := NewFile(path)
// if err != nil {
// return err
// }
//
// with:
//
// c, err := NewFile2(path)
// if err != nil {
// return err
// }
// err = c.Load()
// if err != nil {
// return err
// }
func NewFile(path string) (Capabilities, error) {
c, err := newFile(path)
if err != nil {
return c, err
}
err = c.Load()
return c, err
}
// NewFile2 creates a new initialized Capabilities object for given
// file path. This does not load the process's current capabilities;
// to do that you must call Load explicitly.
func NewFile2(path string) (Capabilities, error) {
return newFile(path)
}
......@@ -103,21 +103,17 @@ func newPid(pid int) (c Capabilities, err error) {
case linuxCapVer1:
p := new(capsV1)
p.hdr.version = capVers
p.hdr.pid = pid
p.hdr.pid = int32(pid)
c = p
case linuxCapVer2, linuxCapVer3:
p := new(capsV3)
p.hdr.version = capVers
p.hdr.pid = pid
p.hdr.pid = int32(pid)
c = p
default:
err = errUnknownVers
return
}
err = c.Load()
if err != nil {
c = nil
}
return
}
......@@ -492,10 +488,6 @@ func (c *capsV3) Apply(kind CapType) (err error) {
func newFile(path string) (c Capabilities, err error) {
c = &capsFile{path: path}
err = c.Load()
if err != nil {
c = nil
}
return
}
......
......@@ -13,7 +13,7 @@ import (
type capHeader struct {
version uint32
pid int
pid int32
}
type capData struct {
......
*.coverprofile
node_modules/
vendor
\ No newline at end of file
此差异已折叠。
## Contributing
Use @urfave/cli to ping the maintainers.
Feel free to put up a pull request to fix a bug or maybe add a feature. We will
give it a code review and make sure that it does not break backwards
compatibility. If collaborators agree that it is in line with
the vision of the project, we will work with you to get the code into
a mergeable state and merge it into the master branch.
If you have contributed something significant to the project, we will most
likely add you as a collaborator. As a collaborator you are given the ability
to merge others pull requests. It is very important that new code does not
break existing code, so be careful about what code you do choose to merge.
If you feel like you have contributed to the project but have not yet been added
as a collaborator, we probably forgot to add you :sweat_smile:. Please open an
issue!
......@@ -8,19 +8,16 @@ clone_folder: c:\gopath\src\github.com\urfave\cli
environment:
GOPATH: C:\gopath
GOVERSION: 1.8.x
PYTHON: C:\Python36-x64
PYTHON_VERSION: 3.6.x
PYTHON_ARCH: 64
GOVERSION: 1.11.x
install:
- set PATH=%GOPATH%\bin;C:\go\bin;%PATH%
- go version
- go env
- go get github.com/urfave/gfmrun/...
- go get -v -t ./...
- set PATH=%GOPATH%\bin;C:\go\bin;%PATH%
- go version
- go env
- go get github.com/urfave/gfmrun/...
- go get -v -t ./...
build_script:
- python runtests vet
- python runtests test
- python runtests gfmrun
- go run build.go vet
- go run build.go test
- go run build.go gfmrun
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册