<commit_before>// Copyright 2016 PLUMgrid
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dnsmon

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	_ "net"
	"net/http"
)

var filterImplC string = `

struct ip6_t {
	unsigned int        ver:4;
	unsigned int        priority:8;
	unsigned int        flow_label:20;
	unsigned short      payload_len;
	unsigned char       next_header;
	unsigned char       hop_limit;
	unsigned long long  src_hi;
	unsigned long long  src_lo;
	unsigned long long  dst_hi;
	unsigned long long  dst_lo;
} BPF_PACKET_HEADER;

struct ip6_opt_t {
	unsigned char  next_header;
	unsigned char  ext_len;
	unsigned char  pad[6];
} BPF_PACKET_HEADER;

struct icmp6_t {
	unsigned char   type;
	unsigned char   code;
	unsigned short  checksum;
} BPF_PACKET_HEADER;

struct Counter {
	u64 rx_pkts;
	u64 tx_pkts;
	u64 rx_bytes;
	u64 tx_bytes;
};

struct Ip4Key {
	u32 src;
	u32 dst;
};

struct Ip6Key {
	u64 src_hi;
	u64 src_lo;
	u64 dst_hi;
	u64 dst_lo;
};

BPF_TABLE("hash", struct Ip4Key, struct Counter, ip4_flows, 10240);
BPF_TABLE("hash", struct Ip6Key, struct Counter, ip6_flows, 10240);

enum direction {
	DIRECTION_IN,
	DIRECTION_OUT,
};

static u8 pop_ipv6_headers(void *skb, u8 **cursor, u8 next_header) {
	struct ip6_opt_t *opt = (void *)(*cursor);

	switch (next_header) {
		case 0: goto opt0;
		case 60: goto opt60_1;
		case 43: goto opt43;
		case 44: goto opt44;
		//case 51: goto opt51;
		//case 50: goto opt50;
		//case 135: goto opt135;
		default: goto DONE;
	}

	opt0: {
		opt = cursor_advance(*cursor, sizeof(*opt));
		u8 ext_len = opt->ext_len << 3;
		cursor_advance(*cursor, ext_len);
		switch (opt->next_header) {
			case 60: goto opt60_1;
			case 43: goto opt43;
			case 44: goto opt44;
			//case 51: goto opt51;
			//case 50: goto opt50;
			//case 135: goto opt135;
			default: goto DONE;
		}
	}

	opt60_1: {
		opt = cursor_advance(*cursor, sizeof(*opt));
		u8 ext_len = opt->ext_len << 3;
		cursor_advance(*cursor, ext_len);
		switch (opt->next_header) {
			case 43: goto opt43;
			case 44: goto opt44;
			//case 51: goto opt51;
			//case 50: goto opt50;
			//case 135: goto opt135;
			default: goto DONE;
		}
	}

	opt43: {
		opt = cursor_advance(*cursor, sizeof(*opt));
		u8 ext_len = opt->ext_len << 3;
		cursor_advance(*cursor, ext_len);
		switch (opt->next_header) {
			case 44: goto opt44;
			//case 51: goto opt51;
			//case 50: goto opt50;
			case 60: goto opt60_2;
			//case 135: goto opt135;
			default: goto DONE;
		}
	}

	opt44: {
		opt = cursor_advance(*cursor, sizeof(*opt));
		switch (opt->next_header) {
			//case 51: goto opt51;
			//case 50: goto opt50;
			case 60: goto opt60_2;
			//case 135: goto opt135;
			default: goto DONE;
		}
	}

#if 0
	opt51: {
		opt = cursor_advance(*cursor, sizeof(*opt));
		u8 ext_len = opt->ext_len << 2;
		cursor_advance(*cursor, ext_len);
		switch (opt->next_header) {
			case 50: goto opt50;
			//case 135: goto opt135;
			case 60: goto opt60_2;
			default: goto DONE;
		}
	}

	opt50: {
		opt = cursor_advance(*cursor, sizeof(*opt));
		u8 ext_len = opt->ext_len << 2;
		cursor_advance(*cursor, ext_len);
		switch (opt->next_header) {
			//case 135: goto opt135;
			case 60: goto opt60_2;
			default: goto DONE;
		}
	}
#endif

	opt60_2: {
		opt = cursor_advance(*cursor, sizeof(*opt));
		u8 ext_len = opt->ext_len << 3;
		cursor_advance(*cursor, ext_len);
		switch (opt->next_header) {
			//case 135: goto opt135;
			default: goto DONE;
		}
	}

#if 0
	opt135: {
		opt = cursor_advance(*cursor, sizeof(*opt));
		u8 ext_len = opt->ext_len << 3;
		cursor_advance(*cursor, ext_len);
		switch (opt->next_header) {
			case 60: goto opt60_2;
			//case 135: goto opt135;
			default: goto DONE;
		}
	}
#endif

DONE:
	return opt->next_header;
}

static int handle_any(void *skb, struct metadata *md, enum direction direction) {
	u8 *cursor = 0;
	int ret = RX_OK;

	struct Counter *val = NULL;
	struct Ip4Key ip4key = {};
	struct Ip6Key ip6key = {};
	bool v4valid = false;
	bool v6valid = false;

	ethernet: {
		struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
		u16 ethertype = ethernet->type;
		switch (ethertype) {
			case ETH_P_IP: goto ip;
			case ETH_P_IPV6: goto ip6;
			default: goto DONE;
		}
	}

	ip: {
		struct ip_t *ip = cursor_advance(cursor, sizeof(*ip));
		if (direction == DIRECTION_OUT) {
			ip4key.src = ip->src;
			ip4key.dst = ip->dst;
		} else {
			ip4key.dst = ip->src;
			ip4key.src = ip->dst;
		}
		v4valid = true;
		u8 hlen_bytes = ip->hlen << 2;
		cursor_advance(cursor, hlen_bytes - sizeof(*ip));
		u8 nextp = ip->nextp;
		switch (nextp) {
			case 6: goto tcp;
			case 17: goto udp;
			default: goto DONE;
		}
	}

	ip6: {
		struct ip6_t *ip6 = cursor_advance(cursor, sizeof(*ip6));
		if (direction == DIRECTION_OUT) {
			ip6key.src_lo = ip6->src_lo;
			ip6key.src_hi = ip6->src_hi;
			ip6key.dst_lo = ip6->dst_lo;
			ip6key.dst_hi = ip6->dst_hi;
		} else {
			ip6key.dst_lo = ip6->src_lo;
			ip6key.dst_hi = ip6->src_hi;
			ip6key.src_lo = ip6->dst_lo;
			ip6key.src_hi = ip6->dst_hi;
		}
		v6valid = true;
		u8 next_header = pop_ipv6_headers(skb, &cursor, ip6->next_header);
		switch (next_header) {
			case 6: goto tcp;
			case 17: goto udp;
			default: goto DONE;
		}
	}

	udp: {
		struct udp_t *udp = cursor_advance(cursor, sizeof(*udp));
		if ((udp->dport == 53 && direction == DIRECTION_IN) ||
				(udp->sport == 53 && direction == DIRECTION_OUT)) {
			goto LOOKUP;
		}
		goto DONE;
	}

	tcp: {
		struct tcp_t *tcp = cursor_advance(cursor, sizeof(*tcp));
		if ((tcp->dst_port == 53 && direction == DIRECTION_IN) ||
				(tcp->src_port == 53 && direction == DIRECTION_OUT)) {
			goto LOOKUP;
		}
		goto DONE;
	}

	LOOKUP: {
		struct Counter zero = {};
		struct Counter *counter = NULL;
		if (v4valid) {
			counter = ip4_flows.lookup_or_init(&ip4key, &zero);
		} else if (v6valid) {
			counter = ip6_flows.lookup_or_init(&ip6key, &zero);
		} else {
			goto DONE;
		}
		if (direction == DIRECTION_OUT) {
			counter->tx_pkts++;
			counter->tx_bytes += md->pktlen;
		} else {
			counter->rx_pkts++;
			counter->rx_bytes += md->pktlen;
		}
	}

DONE:
	return ret;
}

static int handle_rx(void *skb, struct metadata *md) {
	return handle_any(skb, md, DIRECTION_OUT);
}
static int handle_tx(void *skb, struct metadata *md) {
	return handle_any(skb, md, DIRECTION_IN);
}
`

type Dataplane struct {
	client  *http.Client
	baseUrl string
	id      string
}

func NewDataplane() *Dataplane {
	client := &http.Client{}
	d := &Dataplane{
		client: client,
	}

	return d
}

func (d *Dataplane) postObject(url string, requestObj interface{}, responseObj interface{}) (err error) {
	b, err := json.Marshal(requestObj)
	if err != nil {
		return
	}
	resp, err := d.client.Post(d.baseUrl+url, "application/json", bytes.NewReader(b))
	if err != nil {
		return
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		var body []byte
		if body, err = ioutil.ReadAll(resp.Body); err != nil {
			Error.Print(string(body))
		}
		return fmt.Errorf("module server returned %s", resp.Status)
	}
	if responseObj != nil {
		err = json.NewDecoder(resp.Body).Decode(responseObj)
	}
	return
}

type moduleEntry struct {
	Id          string                 `json:"id"`
	ModuleType  string                 `json:"module_type"`
	DisplayName string                 `json:"display_name"`
	Perm        string                 `json:"permissions"`
	Config      map[string]interface{} `json:"config"`
}

func (d *Dataplane) Init(baseUrl string) error {
	d.baseUrl = baseUrl
	req := map[string]interface{}{
		"module_type":  "bpf",
		"display_name": "dnsmon",
		"config": map[string]interface{}{
			"code":     filterImplC,
			"handlers": []string{"handle_rx", "handle_tx"},
		},
	}
	var module moduleEntry
	err := d.postObject("/modules/", req, &module)
	if err != nil {
		return err
	}
	d.id = module.Id
	return nil
}

func (d *Dataplane) Id() string {
	return d.id
}

func (d *Dataplane) Close() error {
	return nil
}
<commit_msg>Remove packet headers<commit_after>// Copyright 2016 PLUMgrid
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dnsmon

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	_ "net"
	"net/http"
)

var filterImplC string = `
struct Counter {
	u64 rx_pkts;
	u64 tx_pkts;
	u64 rx_bytes;
	u64 tx_bytes;
};

struct Ip4Key {
	u32 src;
	u32 dst;
};

struct Ip6Key {
	u64 src_hi;
	u64 src_lo;
	u64 dst_hi;
	u64 dst_lo;
};

BPF_TABLE("hash", struct Ip4Key, struct Counter, ip4_flows, 10240);
BPF_TABLE("hash", struct Ip6Key, struct Counter, ip6_flows, 10240);

enum direction {
	DIRECTION_IN,
	DIRECTION_OUT,
};

static u8 pop_ipv6_headers(void *skb, u8 **cursor, u8 next_header) {
	struct ip6_opt_t *opt = (void *)(*cursor);

	switch (next_header) {
		case 0: goto opt0;
		case 60: goto opt60_1;
		case 43: goto opt43;
		case 44: goto opt44;
		//case 51: goto opt51;
		//case 50: goto opt50;
		//case 135: goto opt135;
		default: goto DONE;
	}

	opt0: {
		opt = cursor_advance(*cursor, sizeof(*opt));
		u8 ext_len = opt->ext_len << 3;
		cursor_advance(*cursor, ext_len);
		switch (opt->next_header) {
			case 60: goto opt60_1;
			case 43: goto opt43;
			case 44: goto opt44;
			//case 51: goto opt51;
			//case 50: goto opt50;
			//case 135: goto opt135;
			default: goto DONE;
		}
	}

	opt60_1: {
		opt = cursor_advance(*cursor, sizeof(*opt));
		u8 ext_len = opt->ext_len << 3;
		cursor_advance(*cursor, ext_len);
		switch (opt->next_header) {
			case 43: goto opt43;
			case 44: goto opt44;
			//case 51: goto opt51;
			//case 50: goto opt50;
			//case 135: goto opt135;
			default: goto DONE;
		}
	}

	opt43: {
		opt = cursor_advance(*cursor, sizeof(*opt));
		u8 ext_len = opt->ext_len << 3;
		cursor_advance(*cursor, ext_len);
		switch (opt->next_header) {
			case 44: goto opt44;
			//case 51: goto opt51;
			//case 50: goto opt50;
			case 60: goto opt60_2;
			//case 135: goto opt135;
			default: goto DONE;
		}
	}

	opt44: {
		opt = cursor_advance(*cursor, sizeof(*opt));
		switch (opt->next_header) {
			//case 51: goto opt51;
			//case 50: goto opt50;
			case 60: goto opt60_2;
			//case 135: goto opt135;
			default: goto DONE;
		}
	}

#if 0
	opt51: {
		opt = cursor_advance(*cursor, sizeof(*opt));
		u8 ext_len = opt->ext_len << 2;
		cursor_advance(*cursor, ext_len);
		switch (opt->next_header) {
			case 50: goto opt50;
			//case 135: goto opt135;
			case 60: goto opt60_2;
			default: goto DONE;
		}
	}

	opt50: {
		opt = cursor_advance(*cursor, sizeof(*opt));
		u8 ext_len = opt->ext_len << 2;
		cursor_advance(*cursor, ext_len);
		switch (opt->next_header) {
			//case 135: goto opt135;
			case 60: goto opt60_2;
			default: goto DONE;
		}
	}
#endif

	opt60_2: {
		opt = cursor_advance(*cursor, sizeof(*opt));
		u8 ext_len = opt->ext_len << 3;
		cursor_advance(*cursor, ext_len);
		switch (opt->next_header) {
			//case 135: goto opt135;
			default: goto DONE;
		}
	}

#if 0
	opt135: {
		opt = cursor_advance(*cursor, sizeof(*opt));
		u8 ext_len = opt->ext_len << 3;
		cursor_advance(*cursor, ext_len);
		switch (opt->next_header) {
			case 60: goto opt60_2;
			//case 135: goto opt135;
			default: goto DONE;
		}
	}
#endif

DONE:
	return opt->next_header;
}

static int handle_any(void *skb, struct metadata *md, enum direction direction) {
	u8 *cursor = 0;
	int ret = RX_OK;

	struct Counter *val = NULL;
	struct Ip4Key ip4key = {};
	struct Ip6Key ip6key = {};
	bool v4valid = false;
	bool v6valid = false;

	ethernet: {
		struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
		u16 ethertype = ethernet->type;
		switch (ethertype) {
			case ETH_P_IP: goto ip;
			case ETH_P_IPV6: goto ip6;
			default: goto DONE;
		}
	}

	ip: {
		struct ip_t *ip = cursor_advance(cursor, sizeof(*ip));
		if (direction == DIRECTION_OUT) {
			ip4key.src = ip->src;
			ip4key.dst = ip->dst;
		} else {
			ip4key.dst = ip->src;
			ip4key.src = ip->dst;
		}
		v4valid = true;
		u8 hlen_bytes = ip->hlen << 2;
		cursor_advance(cursor, hlen_bytes - sizeof(*ip));
		u8 nextp = ip->nextp;
		switch (nextp) {
			case 6: goto tcp;
			case 17: goto udp;
			default: goto DONE;
		}
	}

	ip6: {
		struct ip6_t *ip6 = cursor_advance(cursor, sizeof(*ip6));
		if (direction == DIRECTION_OUT) {
			ip6key.src_lo = ip6->src_lo;
			ip6key.src_hi = ip6->src_hi;
			ip6key.dst_lo = ip6->dst_lo;
			ip6key.dst_hi = ip6->dst_hi;
		} else {
			ip6key.dst_lo = ip6->src_lo;
			ip6key.dst_hi = ip6->src_hi;
			ip6key.src_lo = ip6->dst_lo;
			ip6key.src_hi = ip6->dst_hi;
		}
		v6valid = true;
		u8 next_header = pop_ipv6_headers(skb, &cursor, ip6->next_header);
		switch (next_header) {
			case 6: goto tcp;
			case 17: goto udp;
			default: goto DONE;
		}
	}

	udp: {
		struct udp_t *udp = cursor_advance(cursor, sizeof(*udp));
		if ((udp->dport == 53 && direction == DIRECTION_IN) ||
				(udp->sport == 53 && direction == DIRECTION_OUT)) {
			goto LOOKUP;
		}
		goto DONE;
	}

	tcp: {
		struct tcp_t *tcp = cursor_advance(cursor, sizeof(*tcp));
		if ((tcp->dst_port == 53 && direction == DIRECTION_IN) ||
				(tcp->src_port == 53 && direction == DIRECTION_OUT)) {
			goto LOOKUP;
		}
		goto DONE;
	}

	LOOKUP: {
		struct Counter zero = {};
		struct Counter *counter = NULL;
		if (v4valid) {
			counter = ip4_flows.lookup_or_init(&ip4key, &zero);
		} else if (v6valid) {
			counter = ip6_flows.lookup_or_init(&ip6key, &zero);
		} else {
			goto DONE;
		}
		if (direction == DIRECTION_OUT) {
			counter->tx_pkts++;
			counter->tx_bytes += md->pktlen;
		} else {
			counter->rx_pkts++;
			counter->rx_bytes += md->pktlen;
		}
	}

DONE:
	return ret;
}

static int handle_rx(void *skb, struct metadata *md) {
	return handle_any(skb, md, DIRECTION_OUT);
}
static int handle_tx(void *skb, struct metadata *md) {
	return handle_any(skb, md, DIRECTION_IN);
}
`

type Dataplane struct {
	client  *http.Client
	baseUrl string
	id      string
}

func NewDataplane() *Dataplane {
	client := &http.Client{}
	d := &Dataplane{
		client: client,
	}

	return d
}

func (d *Dataplane) postObject(url string, requestObj interface{}, responseObj interface{}) (err error) {
	b, err := json.Marshal(requestObj)
	if err != nil {
		return
	}
	resp, err := d.client.Post(d.baseUrl+url, "application/json", bytes.NewReader(b))
	if err != nil {
		return
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		var body []byte
		if body, err = ioutil.ReadAll(resp.Body); err != nil {
			Error.Print(string(body))
		}
		return fmt.Errorf("module server returned %s", resp.Status)
	}
	if responseObj != nil {
		err = json.NewDecoder(resp.Body).Decode(responseObj)
	}
	return
}

type moduleEntry struct {
	Id          string                 `json:"id"`
	ModuleType  string                 `json:"module_type"`
	DisplayName string                 `json:"display_name"`
	Perm        string                 `json:"permissions"`
	Config      map[string]interface{} `json:"config"`
}

func (d *Dataplane) Init(baseUrl string) error {
	d.baseUrl = baseUrl
	req := map[string]interface{}{
		"module_type":  "bpf",
		"display_name": "dnsmon",
		"config": map[string]interface{}{
			"code":     filterImplC,
			"handlers": []string{"handle_rx", "handle_tx"},
		},
	}
	var module moduleEntry
	err := d.postObject("/modules/", req, &module)
	if err != nil {
		return err
	}
	d.id = module.Id
	return nil
}

func (d *Dataplane) Id() string {
	return d.id
}

func (d *Dataplane) Close() error {
	return nil
}
<|endoftext|>


<commit_before>// The version package provides a location to set the release versions for all
// packages to consume, without creating import cycles.
//
// This package should not import any other terraform packages.
package version

import (
	"fmt"

	version "github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
var Version = "1.2.0"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
var Prerelease = "alpha20220413"

// SemVer is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVer *version.Version

func init() {
	SemVer = version.Must(version.NewVersion(Version))
}

// Header is the header name used to send the current terraform version
// in http requests.
const Header = "Terraform-Version"

// String returns the complete version string, including prerelease
func String() string {
	if Prerelease != "" {
		return fmt.Sprintf("%s-%s", Version, Prerelease)
	}
	return Version
}
<commit_msg>Cleanup after v1.2.0-alpha20220413 release<commit_after>// The version package provides a location to set the release versions for all
// packages to consume, without creating import cycles.
//
// This package should not import any other terraform packages.
package version

import (
	"fmt"

	version "github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
var Version = "1.2.0"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
var Prerelease = "dev"

// SemVer is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVer *version.Version

func init() {
	SemVer = version.Must(version.NewVersion(Version))
}

// Header is the header name used to send the current terraform version
// in http requests.
const Header = "Terraform-Version"

// String returns the complete version string, including prerelease
func String() string {
	if Prerelease != "" {
		return fmt.Sprintf("%s-%s", Version, Prerelease)
	}
	return Version
}
<|endoftext|>


<commit_before>// Copyright © 2016 Anthony Spring <aspring@yieldbot.com>
//
// 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 main

import (
	"fmt"
)

const AppVersionMajor = "0"
const AppVersionMinor = "0"
const AppVersionPatch = "1"
const AppVersionPre = ""

// Set this to an empty string when releasing
const AppVersionBuild = ""

// This function gene
func AppVersion() string {
	return fmt.Sprintf("%s.%s.%s%s%s", AppVersionMajor, AppVersionMinor, AppVersionPatch, AppVersionPre, AppVersionBuild)
}

func main() {
	fmt.Printf("%s", AppVersion())
}
<commit_msg>debugging new pipeline<commit_after>// Copyright © 2016 Anthony Spring <aspring@yieldbot.com>
//
// 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 main

import (
	"fmt"
)

const AppVersionMajor = "0"
const AppVersionMinor = "1"
const AppVersionPatch = "10"
const AppVersionPre = ""

// Set this to an empty string when releasing
const AppVersionBuild = ""

// This function gene
func AppVersion() string {
	return fmt.Sprintf("%s.%s.%s%s%s", AppVersionMajor, AppVersionMinor, AppVersionPatch, AppVersionPre, AppVersionBuild)
}

func main() {
	fmt.Printf("%s", AppVersion())
}
<|endoftext|>


<commit_before>package version

import (
	"bytes"
	"fmt"
)

// The git commit that was compiled. This will be filled in by the compiler.
var GitCommit string

// The main version number that is being run at the moment.
const Version = "1.5.5"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = ""

func FormattedVersion() string {
	var versionString bytes.Buffer
	fmt.Fprintf(&versionString, "%s", Version)
	if VersionPrerelease != "" {
		fmt.Fprintf(&versionString, "-%s", VersionPrerelease)

		if GitCommit != "" {
			fmt.Fprintf(&versionString, " (%s)", GitCommit)
		}
	}

	return versionString.String()
}
<commit_msg>update to v1.5.6-dev<commit_after>package version

import (
	"bytes"
	"fmt"
)

// The git commit that was compiled. This will be filled in by the compiler.
var GitCommit string

// The main version number that is being run at the moment.
const Version = "1.5.6"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = "dev"

func FormattedVersion() string {
	var versionString bytes.Buffer
	fmt.Fprintf(&versionString, "%s", Version)
	if VersionPrerelease != "" {
		fmt.Fprintf(&versionString, "-%s", VersionPrerelease)

		if GitCommit != "" {
			fmt.Fprintf(&versionString, " (%s)", GitCommit)
		}
	}

	return versionString.String()
}
<|endoftext|>


<commit_before>package version

import "fmt"

const (
	// VersionMajor is for an API incompatible changes
	VersionMajor = 5
	// VersionMinor is for functionality in a backwards-compatible manner
	VersionMinor = 3
	// VersionPatch is for backwards-compatible bug fixes
	VersionPatch = 1

	// VersionDev indicates development branch. Releases will be empty string.
	VersionDev = ""
)

// Version is the specification version that the package types support.
var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev)
<commit_msg>Bump to v5.3.2-dev<commit_after>package version

import "fmt"

const (
	// VersionMajor is for an API incompatible changes
	VersionMajor = 5
	// VersionMinor is for functionality in a backwards-compatible manner
	VersionMinor = 3
	// VersionPatch is for backwards-compatible bug fixes
	VersionPatch = 2

	// VersionDev indicates development branch. Releases will be empty string.
	VersionDev = "-dev"
)

// Version is the specification version that the package types support.
var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev)
<|endoftext|>


<commit_before><commit_msg>track version.go<commit_after>package version
const (
	VERSION_NUMBER = "0.0.23"
)
<|endoftext|>


<commit_before>// Copyright © 2016 Anthony Spring <aspring@yieldbot.com>
//
// 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 version

import (
	"fmt"
)

//AppVersionMajor is the major revision number
const AppVersionMajor = "0"

// AppVersionMinor is the minor revison number
const AppVersionMinor = "0"

// AppVersionPatch is the patch version
const AppVersionPatch = "3"

// AppVersionPre ...
const AppVersionPre = ""

// AppVersionBuild should be empty string when releasing
const AppVersionBuild = ""

// AppVersion generates a usable version string
func AppVersion() string {
	return fmt.Sprintf("%s.%s.%s%s%s", AppVersionMajor, AppVersionMinor, AppVersionPatch, AppVersionPre, AppVersionBuild)
}

func main() {
	fmt.Printf("%s", AppVersion())
}
<commit_msg>[sensupluginssensu]Version Bump<commit_after>// Copyright © 2017 Anthony Spring <aspring@yieldbot.com>
//
// 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 version

import (
	"fmt"
)

//AppVersionMajor is the major revision number
const AppVersionMajor = "0"

// AppVersionMinor is the minor revison number
const AppVersionMinor = "0"

// AppVersionPatch is the patch version
const AppVersionPatch = "4"

// AppVersionPre ...
const AppVersionPre = ""

// AppVersionBuild should be empty string when releasing
const AppVersionBuild = ""

// AppVersion generates a usable version string
func AppVersion() string {
	return fmt.Sprintf("%s.%s.%s%s%s", AppVersionMajor, AppVersionMinor, AppVersionPatch, AppVersionPre, AppVersionBuild)
}

func main() {
	fmt.Printf("%s", AppVersion())
}
<|endoftext|>


<commit_before>package version

// Version is the version of the build.
const Version = "1.3.1-dev"
<commit_msg>Bump version to v1.4.0-dev<commit_after>package version

// Version is the version of the build.
const Version = "1.4.0-dev"
<|endoftext|>


<commit_before><commit_msg>Don't use config file, read as cli opts<commit_after><|endoftext|>


<commit_before>package app

import (
	"fmt"
	humanize "github.com/dustin/go-humanize"
	"github.com/heysquirrel/tribe/git"
	"github.com/heysquirrel/tribe/view"
	"github.com/jroimartin/gocui"
	"io"
	"strconv"
	"strings"
)

type Justification int

const (
	LEFT Justification = 1 + iota
	RIGHT
)

type Column struct {
	name          string
	size          int
	justification Justification
}

type Row []string

type Table struct {
	width   int
	columns []Column
	rows    []Row
}

func NewColumn(name string, size int, justification Justification) Column {
	return Column{name: name, size: size, justification: justification}
}

func (c *Column) Render(data string) string {
	format := " %%-%ds"
	if c.justification == RIGHT {
		format = "%%%ds "
	}
	return fmt.Sprintf(fmt.Sprintf(format, c.size-1), data)
}

func NewTable(width int) *Table {
	table := new(Table)
	table.width = width
	table.columns = make([]Column, 0)
	table.rows = make([]Row, 0)

	return table
}

func (t *Table) AddColumn(name string, size float64, justification Justification) {
	columnSize := int(float64(t.width) * size)
	t.columns = append(t.columns, NewColumn(name, columnSize, justification))
}

func (t *Table) MustAddRow(row Row) {
	if len(t.columns) != len(row) {
		panic("Row size should match column size")
	}

	t.rows = append(t.rows, row)
}

func center(s string, width int) string {
	leftPad := width/2 + len(s)/2

	if leftPad%2 != 0 {
		leftPad = leftPad + 1
	}

	return fmt.Sprintf(fmt.Sprintf("%%-%ds", width), fmt.Sprintf(fmt.Sprintf("%%%ds", leftPad), s))
}

func (t *Table) Render(w io.Writer) {
	maxView := t.width - 2

	header := make([]string, 0)
	for _, column := range t.columns {
		header = append(header, center(column.name, column.size))
	}
	fmt.Fprintln(w, strings.Join(header, "|"))

	fmt.Fprintf(w, "+%s+\n", strings.Repeat("-", maxView))

	for i, row := range t.rows {
		columns := make([]string, 0)

		for j, column := range t.columns {
			data := row[j]
			if i == 0 && j == 0 {
				data = fmt.Sprintf(" 🌶  %s", data)
			}

			columns = append(columns, column.Render(data))
		}

		fmt.Fprintln(w, strings.Join(columns, "|"))
	}
}

func (a *App) UpdateContributors2(contributors []*git.Contributor) {
	a.updateView(contributorsView, func(v *gocui.View) {
		maxX, _ := v.Size()
		table := NewTable(maxX)
		table.AddColumn("NAME", 0.55, LEFT)
		table.AddColumn("COMMITS", 0.2, RIGHT)
		table.AddColumn("LAST COMMIT", 0.25, LEFT)

		for _, contributor := range contributors {
			table.MustAddRow([]string{contributor.Name, strconv.Itoa(contributor.Count), humanize.Time(contributor.LastCommit)})
		}

		table.Render(v)
	})
}

func (a *App) UpdateRelatedFiles(files []*git.RelatedFile) {
	a.updateView(associatedFilesView, func(v *gocui.View) {
		maxX, _ := v.Size()

		table := NewTable(maxX)
		table.AddColumn("NAME", 0.75, LEFT)
		table.AddColumn("COMMITS", 0.1, RIGHT)
		table.AddColumn("LAST COMMIT", 0.15, LEFT)

		for _, file := range files {
			table.MustAddRow([]string{
				view.RenderFilename(file.Name),
				strconv.Itoa(file.Count),
				humanize.Time(file.LastCommit)})
		}

		table.Render(v)
	})
}
<commit_msg>Moved pepper emoji to a constant<commit_after>package app

import (
	"fmt"
	humanize "github.com/dustin/go-humanize"
	"github.com/heysquirrel/tribe/git"
	"github.com/heysquirrel/tribe/view"
	"github.com/jroimartin/gocui"
	"io"
	"strconv"
	"strings"
)

type Justification int

const (
	LEFT Justification = 1 + iota
	RIGHT
)

const (
	HOT = `🌶`
)

type Column struct {
	name          string
	size          int
	justification Justification
}

type Row []string

type Table struct {
	width   int
	columns []Column
	rows    []Row
}

func NewColumn(name string, size int, justification Justification) Column {
	return Column{name: name, size: size, justification: justification}
}

func (c *Column) Render(data string) string {
	format := " %%-%ds"
	if c.justification == RIGHT {
		format = "%%%ds "
	}
	return fmt.Sprintf(fmt.Sprintf(format, c.size-1), data)
}

func NewTable(width int) *Table {
	table := new(Table)
	table.width = width
	table.columns = make([]Column, 0)
	table.rows = make([]Row, 0)

	return table
}

func (t *Table) AddColumn(name string, size float64, justification Justification) {
	columnSize := int(float64(t.width) * size)
	t.columns = append(t.columns, NewColumn(name, columnSize, justification))
}

func (t *Table) MustAddRow(row Row) {
	if len(t.columns) != len(row) {
		panic("Row size should match column size")
	}

	t.rows = append(t.rows, row)
}

func center(s string, width int) string {
	leftPad := width/2 + len(s)/2

	if leftPad%2 != 0 {
		leftPad = leftPad + 1
	}

	return fmt.Sprintf(fmt.Sprintf("%%-%ds", width), fmt.Sprintf(fmt.Sprintf("%%%ds", leftPad), s))
}

func (t *Table) Render(w io.Writer) {
	maxView := t.width - 2

	header := make([]string, 0)
	for _, column := range t.columns {
		header = append(header, center(column.name, column.size))
	}
	fmt.Fprintln(w, strings.Join(header, "|"))

	fmt.Fprintf(w, "+%s+\n", strings.Repeat("-", maxView))

	for i, row := range t.rows {
		columns := make([]string, 0)

		for j, column := range t.columns {
			data := row[j]
			if i == 0 && j == 0 {
				data = fmt.Sprintf(" %s  %s", HOT, data)
			}

			columns = append(columns, column.Render(data))
		}

		fmt.Fprintln(w, strings.Join(columns, "|"))
	}
}

func (a *App) UpdateContributors2(contributors []*git.Contributor) {
	a.updateView(contributorsView, func(v *gocui.View) {
		maxX, _ := v.Size()
		table := NewTable(maxX)
		table.AddColumn("NAME", 0.55, LEFT)
		table.AddColumn("COMMITS", 0.2, RIGHT)
		table.AddColumn("LAST COMMIT", 0.25, LEFT)

		for _, contributor := range contributors {
			table.MustAddRow([]string{contributor.Name, strconv.Itoa(contributor.Count), humanize.Time(contributor.LastCommit)})
		}

		table.Render(v)
	})
}

func (a *App) UpdateRelatedFiles(files []*git.RelatedFile) {
	a.updateView(associatedFilesView, func(v *gocui.View) {
		maxX, _ := v.Size()

		table := NewTable(maxX)
		table.AddColumn("NAME", 0.75, LEFT)
		table.AddColumn("COMMITS", 0.1, RIGHT)
		table.AddColumn("LAST COMMIT", 0.15, LEFT)

		for _, file := range files {
			table.MustAddRow([]string{
				view.RenderFilename(file.Name),
				strconv.Itoa(file.Count),
				humanize.Time(file.LastCommit)})
		}

		table.Render(v)
	})
}
<|endoftext|>


<commit_before>// Copyright 2015 Square Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"encoding/json"
	"fmt"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-fuse/fuse/nodefs"
	"github.com/hanwen/go-fuse/fuse/pathfs"
	"github.com/square/go-sq-metrics"
	"github.com/square/keywhiz-fs/log"
	"golang.org/x/sys/unix"
)

const (
	fsVersion  = "2.0"
	fuseEISDIR = fuse.Status(unix.EISDIR)
)

// Initialized via ldflags
var (
	buildRevision = "unknown"
	buildTime     = "0"
	buildMachine  = "unknown"
)

// StatusInfo contains debug info accessible via `.json/status`.
type StatusInfo struct {
	BuildRevision  string           `json:"build_revision"`
	BuildMachine   string           `json:"build_machine"`
	BuildTime      time.Time        `json:"build_time"`
	StartTime      time.Time        `json:"start_time"`
	RuntimeVersion string           `json:"runtime_version"`
	ServerURL      string           `json:"server_url"`
	ClientParams   httpClientParams `json:"client_params"`
}

// KeywhizFs is the central struct for dispatching filesystem operations.
type KeywhizFs struct {
	pathfs.FileSystem
	*log.Logger
	Client    *Client
	Cache     *Cache
	Metrics   *sqmetrics.SquareMetrics
	StartTime time.Time
	Ownership Ownership
}

func (kwfs KeywhizFs) statusJSON() []byte {
	// Convert buildTime (seconds since epoch) into an actual time.Time object,
	// makes for nicer JSON marshalling (and matches mount time format).
	seconds, err := strconv.ParseInt(buildTime, 10, 64)
	panicOnError(err)

	status, err := json.Marshal(
		StatusInfo{
			BuildRevision:  buildRevision,
			BuildMachine:   buildMachine,
			BuildTime:      time.Unix(seconds, 0),
			StartTime:      kwfs.StartTime,
			RuntimeVersion: runtime.Version(),
			ServerURL:      kwfs.Client.url.String(),
			ClientParams:   kwfs.Client.params,
		})
	panicOnError(err)
	return status
}

func (kwfs KeywhizFs) metricsJSON() []byte {
	if kwfs.Metrics != nil {
		metrics := kwfs.Metrics.SerializeMetrics()
		data, err := json.Marshal(metrics)
		if err == nil {
			return data
		}
		kwfs.Warnf("Error serializing metrics: %v", err)
	}
	return []byte{}
}

// NewKeywhizFs readies a KeywhizFs struct and its parent filesystem objects.
func NewKeywhizFs(client *Client, ownership Ownership, timeouts Timeouts, metrics *sqmetrics.SquareMetrics, logConfig log.Config) (kwfs *KeywhizFs, root nodefs.Node, err error) {
	logger := log.New("kwfs", logConfig)
	cache := NewCache(client, timeouts, logConfig, nil)

	defaultfs := pathfs.NewDefaultFileSystem()            // Returns ENOSYS by default
	readonlyfs := pathfs.NewReadonlyFileSystem(defaultfs) // R/W calls return EPERM

	kwfs = &KeywhizFs{readonlyfs, logger, client, cache, metrics, time.Now(), ownership}
	nfs := pathfs.NewPathNodeFs(kwfs, nil)
	nfs.SetDebug(logConfig.Debug)
	return kwfs, nfs.Root(), nil
}

// GetAttr is a FUSE function which tells FUSE which files and directories exist.
//
// name is empty when getting information on the base directory
func (kwfs KeywhizFs) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
	kwfs.Debugf("GetAttr called with '%v'", name)

	var attr *fuse.Attr
	switch {
	case name == "": // Base directory
		attr = kwfs.directoryAttr(1, 0755) // Writability necessary for .clear_cache
	case name == ".version":
		size := uint64(len(fsVersion))
		attr = kwfs.fileAttr(size, 0444)
	case name == ".clear_cache":
		attr = kwfs.fileAttr(0, 0440)
	case name == ".running":
		size := uint64(len(running()))
		attr = kwfs.fileAttr(size, 0444)
	case name == ".json":
		attr = kwfs.directoryAttr(1, 0700)
	case name == ".json/status":
		size := uint64(len(kwfs.statusJSON()))
		attr = kwfs.fileAttr(size, 0444)
	case name == ".json/metrics":
		size := uint64(len(kwfs.metricsJSON()))
		attr = kwfs.fileAttr(size, 0444)
	case name == ".json/secret":
		attr = kwfs.directoryAttr(0, 0700)
	case name == ".json/secrets":
		data, ok := kwfs.Client.RawSecretList()
		if ok {
			size := uint64(len(data))
			attr = kwfs.fileAttr(size, 0400)
		}
	case strings.HasPrefix(name, ".json/secret/"):
		name = name[len(".json/secret/"):]
		data, err := kwfs.Client.RawSecret(name)
		if err == nil {
			size := uint64(len(data))
			attr = kwfs.fileAttr(size, 0400)
		}
	default:
		secret, ok := kwfs.Cache.Secret(name)
		if ok {
			attr = kwfs.secretAttr(secret)
		}
	}

	if attr != nil {
		return attr, fuse.OK
	}
	return nil, fuse.ENOENT
}

// Open is a FUSE function where an in-memory open file struct is constructed.
func (kwfs KeywhizFs) Open(name string, flags uint32, context *fuse.Context) (nodefs.File, fuse.Status) {
	kwfs.Debugf("Open called with '%v'", name)

	var file nodefs.File
	switch {
	case name == "", name == ".json", name == ".json/secret":
		return nil, fuseEISDIR
	case name == ".version":
		file = nodefs.NewDataFile([]byte(fsVersion))
	case name == ".json/status":
		file = nodefs.NewDataFile(kwfs.statusJSON())
	case name == ".json/metrics":
		file = nodefs.NewDataFile(kwfs.metricsJSON())
	case name == ".clear_cache":
		file = nodefs.NewDevNullFile()
	case name == ".running":
		file = nodefs.NewDataFile(running())
	case name == ".json/secrets":
		data, ok := kwfs.Client.RawSecretList()
		if ok {
			file = nodefs.NewDataFile(data)
		}
	case strings.HasPrefix(name, ".json/secret/"):
		name = name[len(".json/secret/"):]
		data, err := kwfs.Client.RawSecret(name)
		if err == nil {
			file = nodefs.NewDataFile(data)
			kwfs.Infof("Access to %s by uid %d, with gid %d", name, context.Uid, context.Gid)
		}
	default:
		secret, ok := kwfs.Cache.Secret(name)
		if ok {
			file = nodefs.NewDataFile(secret.Content)
			kwfs.Infof("Access to %s by uid %d, with gid %d", name, context.Uid, context.Gid)
		}
	}

	if file != nil {
		file = nodefs.NewReadOnlyFile(file)
		return file, fuse.OK
	}
	return nil, fuse.ENOENT
}

// OpenDir is a FUSE function called when performing a directory listing.
func (kwfs KeywhizFs) OpenDir(name string, context *fuse.Context) (stream []fuse.DirEntry, code fuse.Status) {
	kwfs.Debugf("OpenDir called with '%v'", name)

	var entries []fuse.DirEntry
	switch name {
	case "": // Base directory
		entries = kwfs.secretsDirListing(
			fuse.DirEntry{Name: ".clear_cache", Mode: fuse.S_IFREG},
			fuse.DirEntry{Name: ".json", Mode: fuse.S_IFDIR},
			fuse.DirEntry{Name: ".running", Mode: fuse.S_IFREG},
			fuse.DirEntry{Name: ".version", Mode: fuse.S_IFREG})
	case ".json":
		entries = []fuse.DirEntry{
			{Name: "metrics", Mode: fuse.S_IFREG},
			{Name: "secret", Mode: fuse.S_IFDIR},
			{Name: "secrets", Mode: fuse.S_IFREG},
			{Name: "status", Mode: fuse.S_IFREG},
		}
	case ".json/secret":
		entries = kwfs.secretsDirListing()
	}

	if len(entries) == 0 {
		return entries, fuse.ENOENT
	}
	return entries, fuse.OK
}

// Unlink is a FUSE function called when an object is deleted.
func (kwfs KeywhizFs) Unlink(name string, context *fuse.Context) fuse.Status {
	kwfs.Debugf("Unlink called with '%v'", name)
	if name == ".clear_cache" {
		kwfs.Cache.Clear()
		return fuse.OK
	}
	return fuse.EACCES
}

// StatFs is a FUSE function called to provide information about the filesystem
// We return zeros, which makes "df" think this is a dummy fs, which it is.
func (kwfs KeywhizFs) StatFs(name string) *fuse.StatfsOut {
	kwfs.Debugf("StatFs called with '%v'", name)
	return &fuse.StatfsOut{}
}

// secretsDirListing produces directory entries containing all secret files. Extra entries passed
// to this function are included.
func (kwfs KeywhizFs) secretsDirListing(extraEntries ...fuse.DirEntry) []fuse.DirEntry {
	secrets := kwfs.Cache.SecretList()
	entries := make([]fuse.DirEntry, 0, len(secrets)+len(extraEntries))
	for _, s := range secrets {
		entries = append(entries, fuse.DirEntry{Name: s.Name, Mode: fuse.S_IFREG})
	}
	entries = append(entries, extraEntries...)
	return entries
}

// secretAttr constructs a fuse.Attr based on a given Secret.
func (kwfs KeywhizFs) secretAttr(s *Secret) *fuse.Attr {
	created := uint64(s.CreatedAt.Unix())
	attr := &fuse.Attr{
		Size: s.Length,
		// The resolution for nsec time (uint32) is too small.
		Atime: created,
		Mtime: created,
		Ctime: created,
		Mode:  s.ModeValue(),
	}

	attr.Uid = kwfs.Ownership.Uid
	attr.Gid = kwfs.Ownership.Gid

	if s.Owner != "" {
		attr.Uid = lookupUid(s.Owner)
	}
	if s.Group != "" {
		attr.Gid = lookupGid(s.Group)
	}
	return attr
}

// fileAttr constructs a generic file fuse.Attr with the given parameters.
func (kwfs KeywhizFs) fileAttr(size uint64, mode uint32) *fuse.Attr {
	created := uint64(kwfs.StartTime.Unix())
	attr := fuse.Attr{
		Size:  size,
		Atime: created,
		Mtime: created,
		Ctime: created,
		Mode:  fuse.S_IFREG | mode,
	}
	attr.Uid = kwfs.Ownership.Uid
	attr.Gid = kwfs.Ownership.Gid
	return &attr
}

// directoryAttr constructs a generic directory fuse.Attr with the given parameters.
func (kwfs KeywhizFs) directoryAttr(subdirCount, mode uint32) *fuse.Attr {
	// 4K is typically the minimum size of inode storage for a directory.
	const directoryInodeSize = 4096
	created := uint64(kwfs.StartTime.Unix())

	attr := fuse.Attr{
		Size:  directoryInodeSize,
		Atime: created,
		Mtime: created,
		Ctime: created,
		Mode:  fuse.S_IFDIR | mode,
		Nlink: 2 + subdirCount, // '.', '..', and any other subdirectories
	}
	attr.Uid = kwfs.Ownership.Uid
	attr.Gid = kwfs.Ownership.Gid
	return &attr
}

// running provides a formatted string with the current process ID.
func running() []byte {
	return []byte(fmt.Sprintf("pid=%d", os.Getpid()))
}

func (kwfs KeywhizFs) String() string {
	return "keywhiz-fs"
}
<commit_msg>Add a wrapper class that remembers what mode a file has<commit_after>// Copyright 2015 Square Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"encoding/json"
	"fmt"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-fuse/fuse/nodefs"
	"github.com/hanwen/go-fuse/fuse/pathfs"
	"github.com/square/go-sq-metrics"
	"github.com/square/keywhiz-fs/log"
	"golang.org/x/sys/unix"
)

const (
	fsVersion  = "2.0"
	fuseEISDIR = fuse.Status(unix.EISDIR)
)

// Initialized via ldflags
var (
	buildRevision = "unknown"
	buildTime     = "0"
	buildMachine  = "unknown"
)

// StatusInfo contains debug info accessible via `.json/status`.
type StatusInfo struct {
	BuildRevision  string           `json:"build_revision"`
	BuildMachine   string           `json:"build_machine"`
	BuildTime      time.Time        `json:"build_time"`
	StartTime      time.Time        `json:"start_time"`
	RuntimeVersion string           `json:"runtime_version"`
	ServerURL      string           `json:"server_url"`
	ClientParams   httpClientParams `json:"client_params"`
}

// KeywhizFs is the central struct for dispatching filesystem operations.
type KeywhizFs struct {
	pathfs.FileSystem
	*log.Logger
	Client    *Client
	Cache     *Cache
	Metrics   *sqmetrics.SquareMetrics
	StartTime time.Time
	Ownership Ownership
}

func (kwfs KeywhizFs) statusJSON() []byte {
	// Convert buildTime (seconds since epoch) into an actual time.Time object,
	// makes for nicer JSON marshalling (and matches mount time format).
	seconds, err := strconv.ParseInt(buildTime, 10, 64)
	panicOnError(err)

	status, err := json.Marshal(
		StatusInfo{
			BuildRevision:  buildRevision,
			BuildMachine:   buildMachine,
			BuildTime:      time.Unix(seconds, 0),
			StartTime:      kwfs.StartTime,
			RuntimeVersion: runtime.Version(),
			ServerURL:      kwfs.Client.url.String(),
			ClientParams:   kwfs.Client.params,
		})
	panicOnError(err)
	return status
}

func (kwfs KeywhizFs) metricsJSON() []byte {
	if kwfs.Metrics != nil {
		metrics := kwfs.Metrics.SerializeMetrics()
		data, err := json.Marshal(metrics)
		if err == nil {
			return data
		}
		kwfs.Warnf("Error serializing metrics: %v", err)
	}
	return []byte{}
}

// NewKeywhizFs readies a KeywhizFs struct and its parent filesystem objects.
func NewKeywhizFs(client *Client, ownership Ownership, timeouts Timeouts, metrics *sqmetrics.SquareMetrics, logConfig log.Config) (kwfs *KeywhizFs, root nodefs.Node, err error) {
	logger := log.New("kwfs", logConfig)
	cache := NewCache(client, timeouts, logConfig, nil)

	defaultfs := pathfs.NewDefaultFileSystem()            // Returns ENOSYS by default
	readonlyfs := pathfs.NewReadonlyFileSystem(defaultfs) // R/W calls return EPERM

	kwfs = &KeywhizFs{readonlyfs, logger, client, cache, metrics, time.Now(), ownership}
	nfs := pathfs.NewPathNodeFs(kwfs, nil)
	nfs.SetDebug(logConfig.Debug)
	return kwfs, nfs.Root(), nil
}

// GetAttr is a FUSE function which tells FUSE which files and directories exist.
//
// name is empty when getting information on the base directory
func (kwfs KeywhizFs) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
	kwfs.Debugf("GetAttr called with '%v'", name)

	var attr *fuse.Attr
	switch {
	case name == "": // Base directory
		attr = kwfs.directoryAttr(1, 0755) // Writability necessary for .clear_cache
	case name == ".version":
		size := uint64(len(fsVersion))
		attr = kwfs.fileAttr(size, 0444)
	case name == ".clear_cache":
		attr = kwfs.fileAttr(0, 0440)
	case name == ".running":
		size := uint64(len(running()))
		attr = kwfs.fileAttr(size, 0444)
	case name == ".json":
		attr = kwfs.directoryAttr(1, 0700)
	case name == ".json/status":
		size := uint64(len(kwfs.statusJSON()))
		attr = kwfs.fileAttr(size, 0444)
	case name == ".json/metrics":
		size := uint64(len(kwfs.metricsJSON()))
		attr = kwfs.fileAttr(size, 0444)
	case name == ".json/secret":
		attr = kwfs.directoryAttr(0, 0700)
	case name == ".json/secrets":
		data, ok := kwfs.Client.RawSecretList()
		if ok {
			size := uint64(len(data))
			attr = kwfs.fileAttr(size, 0400)
		}
	case strings.HasPrefix(name, ".json/secret/"):
		name = name[len(".json/secret/"):]
		data, err := kwfs.Client.RawSecret(name)
		if err == nil {
			size := uint64(len(data))
			attr = kwfs.fileAttr(size, 0400)
		}
	default:
		secret, ok := kwfs.Cache.Secret(name)
		if ok {
			attr = kwfs.secretAttr(secret)
		}
	}

	if attr != nil {
		return attr, fuse.OK
	}
	return nil, fuse.ENOENT
}

// Open is a FUSE function where an in-memory open file struct is constructed.
func (kwfs KeywhizFs) Open(name string, flags uint32, context *fuse.Context) (nodefs.File, fuse.Status) {
	kwfs.Debugf("Open called with '%v'", name)

	var file nodefs.File
	switch {
	case name == "", name == ".json", name == ".json/secret":
		return nil, fuseEISDIR
	case name == ".version":
		file = nodefs.NewDataFile([]byte(fsVersion))
	case name == ".json/status":
		file = nodefs.NewDataFile(kwfs.statusJSON())
	case name == ".json/metrics":
		file = nodefs.NewDataFile(kwfs.metricsJSON())
	case name == ".clear_cache":
		file = nodefs.NewDevNullFile()
	case name == ".running":
		file = nodefs.NewDataFile(running())
	case name == ".json/secrets":
		data, ok := kwfs.Client.RawSecretList()
		if ok {
			file = nodefs.NewDataFile(data)
		}
	case strings.HasPrefix(name, ".json/secret/"):
		name = name[len(".json/secret/"):]
		data, err := kwfs.Client.RawSecret(name)
		if err == nil {
			file = nodefs.NewDataFile(data)
			kwfs.Infof("Access to %s by uid %d, with gid %d", name, context.Uid, context.Gid)
		}
	default:
		secret, ok := kwfs.Cache.Secret(name)
		if ok {
			file = nodefs.NewDataFile(secret.Content)
			kwfs.Infof("Access to %s by uid %d, with gid %d", name, context.Uid, context.Gid)
		}
	}

	if file != nil {
		file = nodefs.NewReadOnlyFile(file)
		return file, fuse.OK
	}
	return nil, fuse.ENOENT
}

// OpenDir is a FUSE function called when performing a directory listing.
func (kwfs KeywhizFs) OpenDir(name string, context *fuse.Context) (stream []fuse.DirEntry, code fuse.Status) {
	kwfs.Debugf("OpenDir called with '%v'", name)

	var entries []fuse.DirEntry
	switch name {
	case "": // Base directory
		entries = kwfs.secretsDirListing(
			fuse.DirEntry{Name: ".clear_cache", Mode: fuse.S_IFREG},
			fuse.DirEntry{Name: ".json", Mode: fuse.S_IFDIR},
			fuse.DirEntry{Name: ".running", Mode: fuse.S_IFREG},
			fuse.DirEntry{Name: ".version", Mode: fuse.S_IFREG})
	case ".json":
		entries = []fuse.DirEntry{
			{Name: "metrics", Mode: fuse.S_IFREG},
			{Name: "secret", Mode: fuse.S_IFDIR},
			{Name: "secrets", Mode: fuse.S_IFREG},
			{Name: "status", Mode: fuse.S_IFREG},
		}
	case ".json/secret":
		entries = kwfs.secretsDirListing()
	}

	if len(entries) == 0 {
		return entries, fuse.ENOENT
	}
	return entries, fuse.OK
}

// Unlink is a FUSE function called when an object is deleted.
func (kwfs KeywhizFs) Unlink(name string, context *fuse.Context) fuse.Status {
	kwfs.Debugf("Unlink called with '%v'", name)
	if name == ".clear_cache" {
		kwfs.Cache.Clear()
		return fuse.OK
	}
	return fuse.EACCES
}

// StatFs is a FUSE function called to provide information about the filesystem
// We return zeros, which makes "df" think this is a dummy fs, which it is.
func (kwfs KeywhizFs) StatFs(name string) *fuse.StatfsOut {
	kwfs.Debugf("StatFs called with '%v'", name)
	return &fuse.StatfsOut{}
}

// secretsDirListing produces directory entries containing all secret files. Extra entries passed
// to this function are included.
func (kwfs KeywhizFs) secretsDirListing(extraEntries ...fuse.DirEntry) []fuse.DirEntry {
	secrets := kwfs.Cache.SecretList()
	entries := make([]fuse.DirEntry, 0, len(secrets)+len(extraEntries))
	for _, s := range secrets {
		entries = append(entries, fuse.DirEntry{Name: s.Name, Mode: fuse.S_IFREG})
	}
	entries = append(entries, extraEntries...)
	return entries
}

// secretAttr constructs a fuse.Attr based on a given Secret.
func (kwfs KeywhizFs) secretAttr(s *Secret) *fuse.Attr {
	created := uint64(s.CreatedAt.Unix())
	attr := &fuse.Attr{
		Size: s.Length,
		// The resolution for nsec time (uint32) is too small.
		Atime: created,
		Mtime: created,
		Ctime: created,
		Mode:  s.ModeValue(),
	}

	attr.Uid = kwfs.Ownership.Uid
	attr.Gid = kwfs.Ownership.Gid

	if s.Owner != "" {
		attr.Uid = lookupUid(s.Owner)
	}
	if s.Group != "" {
		attr.Gid = lookupGid(s.Group)
	}
	return attr
}

// fileAttr constructs a generic file fuse.Attr with the given parameters.
func (kwfs KeywhizFs) fileAttr(size uint64, mode uint32) *fuse.Attr {
	created := uint64(kwfs.StartTime.Unix())
	attr := fuse.Attr{
		Size:  size,
		Atime: created,
		Mtime: created,
		Ctime: created,
		Mode:  fuse.S_IFREG | mode,
	}
	attr.Uid = kwfs.Ownership.Uid
	attr.Gid = kwfs.Ownership.Gid
	return &attr
}

// directoryAttr constructs a generic directory fuse.Attr with the given parameters.
func (kwfs KeywhizFs) directoryAttr(subdirCount, mode uint32) *fuse.Attr {
	// 4K is typically the minimum size of inode storage for a directory.
	const directoryInodeSize = 4096
	created := uint64(kwfs.StartTime.Unix())

	attr := fuse.Attr{
		Size:  directoryInodeSize,
		Atime: created,
		Mtime: created,
		Ctime: created,
		Mode:  fuse.S_IFDIR | mode,
		Nlink: 2 + subdirCount, // '.', '..', and any other subdirectories
	}
	attr.Uid = kwfs.Ownership.Uid
	attr.Gid = kwfs.Ownership.Gid
	return &attr
}

// NewModeFile wraps a File so all GetAttr operations return a
// constant Mode
func NewModeFile(f nodefs.File, mode uint32) nodefs.File {
	return &modeFile{File: f, mode: mode}
}

type modeFile struct {
	nodefs.File
	mode uint32
}

func (f *modeFile) InnerFile() nodefs.File {
	return f.File
}

func (f *modeFile) String() string {
	return fmt.Sprintf("modeFile(%s)", f.File.String())
}

func (f *modeFile) GetAttr(out *fuse.Attr) fuse.Status {
	status := f.File.GetAttr(out)
	out.Mode = f.mode
	return status
}

// running provides a formatted string with the current process ID.
func running() []byte {
	return []byte(fmt.Sprintf("pid=%d", os.Getpid()))
}

func (kwfs KeywhizFs) String() string {
	return "keywhiz-fs"
}
<|endoftext|>


<commit_before>package main

import (
	"os"

	"fmt"

	"strings"

	"strconv"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	. "github.com/claudetech/loggo/default"
	"golang.org/x/net/context"
)

// Mount the fuse volume
func Mount(client *Drive, mountpoint string, mountOptions []string, uid, gid uint32, umask os.FileMode) error {
	Log.Infof("Mounting path %v", mountpoint)

	if _, err := os.Stat(mountpoint); os.IsNotExist(err) {
		Log.Debugf("Mountpoint doesn't exist, creating...")
		if err := os.MkdirAll(mountpoint, 0644); nil != err {
			Log.Debugf("%v", err)
			return fmt.Errorf("Could not create mount directory %v", mountpoint)
		}
	}

	fuse.Debug = func(msg interface{}) {
		Log.Tracef("FUSE %v", msg)
	}

	// Set mount options
	options := []fuse.MountOption{
		fuse.NoAppleDouble(),
		fuse.NoAppleXattr(),
	}
	for _, option := range mountOptions {
		if "allow_other" == option {
			options = append(options, fuse.AllowOther())
		} else if "allow_root" == option {
			options = append(options, fuse.AllowRoot())
		} else if "allow_dev" == option {
			options = append(options, fuse.AllowDev())
		} else if "allow_non_empty_mount" == option {
			options = append(options, fuse.AllowNonEmptyMount())
		} else if "allow_suid" == option {
			options = append(options, fuse.AllowSUID())
		} else if strings.Contains(option, "max_readahead=") {
			data := strings.Split(option, "=")
			value, err := strconv.ParseUint(data[1], 10, 32)
			if nil != err {
				Log.Debugf("%v", err)
				return fmt.Errorf("Could not parse max_readahead value")
			}
			options = append(options, fuse.MaxReadahead(uint32(value)))
		} else if "default_permissions" == option {
			options = append(options, fuse.DefaultPermissions())
		} else if "excl_create" == option {
			options = append(options, fuse.ExclCreate())
		} else if strings.Contains(option, "fs_name") {
			data := strings.Split(option, "=")
			options = append(options, fuse.FSName(data[1]))
		} else if "local_volume" == option {
			options = append(options, fuse.LocalVolume())
		} else if "writeback_cache" == option {
			options = append(options, fuse.WritebackCache())
		} else if strings.Contains(option, "volume_name") {
			data := strings.Split(option, "=")
			options = append(options, fuse.VolumeName(data[1]))
		} else if "read_only" == option {
			options = append(options, fuse.ReadOnly())
		} else {
			Log.Warningf("Fuse option %v is not supported, yet", option)
		}
	}

	c, err := fuse.Mount(mountpoint, options...)
	if err != nil {
		return err
	}
	defer c.Close()

	filesys := &FS{
		client: client,
		uid:    uid,
		gid:    gid,
		umask:  umask,
	}
	if err := fs.Serve(c, filesys); err != nil {
		return err
	}

	// check if the mount process has an error to report
	<-c.Ready
	if err := c.MountError; nil != err {
		Log.Debugf("%v", err)
		return fmt.Errorf("Error mounting FUSE")
	}

	return Unmount(mountpoint, true)
}

// Unmount unmounts the mountpoint
func Unmount(mountpoint string, notify bool) error {
	if notify {
		Log.Infof("Unmounting path %v", mountpoint)
	}
	fuse.Unmount(mountpoint)
	return nil
}

// FS the fuse filesystem
type FS struct {
	client *Drive
	uid    uint32
	gid    uint32
	umask  os.FileMode
}

// Root returns the root path
func (f *FS) Root() (fs.Node, error) {
	object, err := f.client.GetRoot()
	if nil != err {
		Log.Warningf("%v", err)
		return nil, fmt.Errorf("Could not get root object")
	}
	return &Object{
		client: f.client,
		object: object,
		uid:    f.uid,
		gid:    f.gid,
		umask:  f.umask,
	}, nil
}

// Object represents one drive object
type Object struct {
	client *Drive
	object *APIObject
	buffer *Buffer
	uid    uint32
	gid    uint32
	umask  os.FileMode
}

// Attr returns the attributes for a directory
func (o *Object) Attr(ctx context.Context, attr *fuse.Attr) error {
	if o.object.IsDir {
		if o.umask > 0 {
			attr.Mode = os.ModeDir | o.umask
		} else {
			attr.Mode = os.ModeDir | 0755
		}
		attr.Size = 0
	} else {
		if o.umask > 0 {
			attr.Mode = o.umask
		} else {
			attr.Mode = 0644
		}
		attr.Size = o.object.Size
	}

	attr.Uid = uint32(o.uid)
	attr.Gid = uint32(o.gid)

	attr.Mtime = o.object.LastModified
	attr.Crtime = o.object.LastModified
	attr.Ctime = o.object.LastModified

	return nil
}

// ReadDirAll shows all files in the current directory
func (o *Object) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
	objects, err := o.client.GetObjectsByParent(o.object.ObjectID)
	if nil != err {
		Log.Debugf("%v", err)
		return nil, fuse.ENOENT
	}

	dirs := []fuse.Dirent{}
	for _, object := range objects {
		dirs = append(dirs, fuse.Dirent{
			Name: object.Name,
			Type: fuse.DT_File,
		})
	}
	return dirs, nil
}

// Lookup tests if a file is existent in the current directory
func (o *Object) Lookup(ctx context.Context, name string) (fs.Node, error) {
	object, err := o.client.GetObjectByParentAndName(o.object.ObjectID, name)
	if nil != err {
		Log.Tracef("%v", err)
		return nil, fuse.ENOENT
	}

	return &Object{
		client: o.client,
		object: object,
		uid:    o.uid,
		gid:    o.gid,
		umask:  o.umask,
	}, nil
}

// Open opens a file for reading
func (o *Object) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
	if req.Dir {
		return o, nil
	}

	buffer, err := o.client.Open(o.object)
	if nil != err {
		Log.Warningf("%v", err)
		return o, fuse.ENOENT
	}
	o.buffer = buffer

	return o, nil
}

// Release a stream
func (o *Object) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
	if nil != o.buffer {
		if err := o.buffer.Close(); nil != err {
			Log.Debugf("%v", err)
			Log.Warningf("Could not close buffer stream")
		}
	}
	return nil
}

// Read reads some bytes or the whole file
func (o *Object) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
	buf, err := o.buffer.ReadBytes(req.Offset, int64(req.Size), false)
	if nil != err {
		Log.Warningf("%v", err)
		return fuse.EIO
	}

	resp.Data = buf[:]
	return nil
}

// Remove deletes an element
func (o *Object) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
	obj, err := o.client.GetObjectByParentAndName(o.object.ObjectID, req.Name)
	if nil != err {
		Log.Warningf("%v", err)
		return fuse.EIO
	}

	err = o.client.Remove(obj, o.object.ObjectID)
	if nil != err {
		Log.Warningf("%v", err)
		return fuse.EIO
	}

	return nil
}
<commit_msg>fixed directory listing<commit_after>package main

import (
	"os"

	"fmt"

	"strings"

	"strconv"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	. "github.com/claudetech/loggo/default"
	"golang.org/x/net/context"
)

// Mount the fuse volume
func Mount(client *Drive, mountpoint string, mountOptions []string, uid, gid uint32, umask os.FileMode) error {
	Log.Infof("Mounting path %v", mountpoint)

	if _, err := os.Stat(mountpoint); os.IsNotExist(err) {
		Log.Debugf("Mountpoint doesn't exist, creating...")
		if err := os.MkdirAll(mountpoint, 0644); nil != err {
			Log.Debugf("%v", err)
			return fmt.Errorf("Could not create mount directory %v", mountpoint)
		}
	}

	fuse.Debug = func(msg interface{}) {
		Log.Tracef("FUSE %v", msg)
	}

	// Set mount options
	options := []fuse.MountOption{
		fuse.NoAppleDouble(),
		fuse.NoAppleXattr(),
	}
	for _, option := range mountOptions {
		if "allow_other" == option {
			options = append(options, fuse.AllowOther())
		} else if "allow_root" == option {
			options = append(options, fuse.AllowRoot())
		} else if "allow_dev" == option {
			options = append(options, fuse.AllowDev())
		} else if "allow_non_empty_mount" == option {
			options = append(options, fuse.AllowNonEmptyMount())
		} else if "allow_suid" == option {
			options = append(options, fuse.AllowSUID())
		} else if strings.Contains(option, "max_readahead=") {
			data := strings.Split(option, "=")
			value, err := strconv.ParseUint(data[1], 10, 32)
			if nil != err {
				Log.Debugf("%v", err)
				return fmt.Errorf("Could not parse max_readahead value")
			}
			options = append(options, fuse.MaxReadahead(uint32(value)))
		} else if "default_permissions" == option {
			options = append(options, fuse.DefaultPermissions())
		} else if "excl_create" == option {
			options = append(options, fuse.ExclCreate())
		} else if strings.Contains(option, "fs_name") {
			data := strings.Split(option, "=")
			options = append(options, fuse.FSName(data[1]))
		} else if "local_volume" == option {
			options = append(options, fuse.LocalVolume())
		} else if "writeback_cache" == option {
			options = append(options, fuse.WritebackCache())
		} else if strings.Contains(option, "volume_name") {
			data := strings.Split(option, "=")
			options = append(options, fuse.VolumeName(data[1]))
		} else if "read_only" == option {
			options = append(options, fuse.ReadOnly())
		} else {
			Log.Warningf("Fuse option %v is not supported, yet", option)
		}
	}

	c, err := fuse.Mount(mountpoint, options...)
	if err != nil {
		return err
	}
	defer c.Close()

	filesys := &FS{
		client: client,
		uid:    uid,
		gid:    gid,
		umask:  umask,
	}
	if err := fs.Serve(c, filesys); err != nil {
		return err
	}

	// check if the mount process has an error to report
	<-c.Ready
	if err := c.MountError; nil != err {
		Log.Debugf("%v", err)
		return fmt.Errorf("Error mounting FUSE")
	}

	return Unmount(mountpoint, true)
}

// Unmount unmounts the mountpoint
func Unmount(mountpoint string, notify bool) error {
	if notify {
		Log.Infof("Unmounting path %v", mountpoint)
	}
	fuse.Unmount(mountpoint)
	return nil
}

// FS the fuse filesystem
type FS struct {
	client *Drive
	uid    uint32
	gid    uint32
	umask  os.FileMode
}

// Root returns the root path
func (f *FS) Root() (fs.Node, error) {
	object, err := f.client.GetRoot()
	if nil != err {
		Log.Warningf("%v", err)
		return nil, fmt.Errorf("Could not get root object")
	}
	return &Object{
		client: f.client,
		object: object,
		uid:    f.uid,
		gid:    f.gid,
		umask:  f.umask,
	}, nil
}

// Object represents one drive object
type Object struct {
	client *Drive
	object *APIObject
	buffer *Buffer
	uid    uint32
	gid    uint32
	umask  os.FileMode
}

// Attr returns the attributes for a directory
func (o *Object) Attr(ctx context.Context, attr *fuse.Attr) error {
	if o.object.IsDir {
		if o.umask > 0 {
			attr.Mode = os.ModeDir | o.umask
		} else {
			attr.Mode = os.ModeDir | 0755
		}
		attr.Size = 0
	} else {
		if o.umask > 0 {
			attr.Mode = o.umask
		} else {
			attr.Mode = 0644
		}
		attr.Size = o.object.Size
	}

	attr.Uid = uint32(o.uid)
	attr.Gid = uint32(o.gid)

	attr.Mtime = o.object.LastModified
	attr.Crtime = o.object.LastModified
	attr.Ctime = o.object.LastModified

	attr.Blocks = (attr.Size + 511) / 512

	return nil
}

// ReadDirAll shows all files in the current directory
func (o *Object) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
	objects, err := o.client.GetObjectsByParent(o.object.ObjectID)
	if nil != err {
		Log.Debugf("%v", err)
		return nil, fuse.ENOENT
	}

	dirs := []fuse.Dirent{}
	for _, object := range objects {
		if object.IsDir {
			dirs = append(dirs, fuse.Dirent{
				Name: object.Name,
				Type: fuse.DT_Dir,
			})
		} else {
			dirs = append(dirs, fuse.Dirent{
				Name: object.Name,
				Type: fuse.DT_File,
			})
		}
	}
	return dirs, nil
}

// Lookup tests if a file is existent in the current directory
func (o *Object) Lookup(ctx context.Context, name string) (fs.Node, error) {
	object, err := o.client.GetObjectByParentAndName(o.object.ObjectID, name)
	if nil != err {
		Log.Tracef("%v", err)
		return nil, fuse.ENOENT
	}

	return &Object{
		client: o.client,
		object: object,
		uid:    o.uid,
		gid:    o.gid,
		umask:  o.umask,
	}, nil
}

// Open opens a file for reading
func (o *Object) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
	if req.Dir {
		return o, nil
	}

	buffer, err := o.client.Open(o.object)
	if nil != err {
		Log.Warningf("%v", err)
		return o, fuse.ENOENT
	}
	o.buffer = buffer

	return o, nil
}

// Release a stream
func (o *Object) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
	if nil != o.buffer {
		if err := o.buffer.Close(); nil != err {
			Log.Debugf("%v", err)
			Log.Warningf("Could not close buffer stream")
		}
	}
	return nil
}

// Read reads some bytes or the whole file
func (o *Object) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
	buf, err := o.buffer.ReadBytes(req.Offset, int64(req.Size), false)
	if nil != err {
		Log.Warningf("%v", err)
		return fuse.EIO
	}

	resp.Data = buf[:]
	return nil
}

// Remove deletes an element
func (o *Object) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
	obj, err := o.client.GetObjectByParentAndName(o.object.ObjectID, req.Name)
	if nil != err {
		Log.Warningf("%v", err)
		return fuse.EIO
	}

	err = o.client.Remove(obj, o.object.ObjectID)
	if nil != err {
		Log.Warningf("%v", err)
		return fuse.EIO
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/t3rm1n4l/go-mega"
	"html"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
)

const CACHEDIR = "cache"
var megaSession *mega.Mega

func handle(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		node, err := node(r.URL.Path)
		if err != nil {
			if err.Error() == "Object (typically, node or user) not found" {
				w.WriteHeader(http.StatusNotFound)
				return
			}
			log.Print(err)
			return
		}
		switch node.GetType() {
		case mega.FOLDER, mega.ROOT:
			list(w, r, node)
		default:
			get(w, r, node)
		}

	case "PUT":
		put(w, r)
	}
}

func list(w http.ResponseWriter, r *http.Request, node *mega.Node) {
	children, err := megaSession.FS.GetChildren(node)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		log.Print(err)
		return
	}

	fmt.Fprint(w, "")
	fmt.Fprintf(w, "
%s
", html.EscapeString(r.URL.Path))
	fmt.Fprint(w, "
")
	if node != megaSession.FS.GetRoot() {
		fmt.Fprintf(w, "
..")
	}
	for _, child := range children {
		var folder string
		if child.GetType() == mega.FOLDER {
			folder = "/"
		}
		fmt.Fprintf(w, "
%s%s",
			html.EscapeString(child.GetName()), folder,
			html.EscapeString(child.GetName()), folder)
	}
	fmt.Fprint(w, "
")
}

func get(w http.ResponseWriter, r *http.Request, node *mega.Node) {
	cachefile := CACHEDIR + r.URL.Path
	dir, _ := path.Split(cachefile)

	// Create local path
	if err := os.MkdirAll(dir, 0700); err != nil && !os.IsExist(err) {
		log.Print(err)
		return
	}
	file, err := os.Open(cachefile)
	if err != nil && os.IsNotExist(err) {
		if err = megaSession.DownloadFile(node, cachefile, nil); err != nil {
			log.Print(err)
			w.WriteHeader(http.StatusInternalServerError)
			// Remove incomplete cachefile, in case one was created
			os.Remove(cachefile)
			return
		}
		file, err = os.Open(cachefile)
		if err != nil {
			log.Print(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
	}
	defer file.Close()
	_, err = io.Copy(w, file)
	if err != nil {
		log.Print(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func put(w http.ResponseWriter, r *http.Request) {
	cachefile := CACHEDIR + r.URL.Path
	dir, name := path.Split(cachefile)

	// Create local file
	if err := os.MkdirAll(dir, 0700); err != nil && !os.IsExist(err) {
		log.Print(err)
		return
	}
	fp, err := os.Create(cachefile)
	if err != nil {
		log.Print(err)
		return
	}
	_, err = io.Copy(fp, r.Body)
	if err != nil {
		log.Print(err)
		return
	}

	// Create Mega path
	dirarray := strings.Split(r.URL.Path, "/")
	root := megaSession.FS.GetRoot()
	n, err := mkpath(dirarray[1:len(dirarray)-1], root)
	if err != nil {
		log.Print(err)
		return
	}

	// Lookup Mega file (if it exists)
	paths, err := megaSession.FS.PathLookup(root, dirarray[1:])
	// Log unexpected errors
	if err != nil && err.Error() != "Object (typically, node or user) not found" {
		log.Print(err)
		return
	}
	// File exists, so delete it before uploading new file
	if err == nil {
		// We only care about the last node.
		lastnode := paths[len(paths)-1]
		// File exists, delete! (aka overwrite)
		if err = megaSession.Delete(lastnode, false); err != nil {
			log.Print(err)
			return
		}
	}
	// Finally, upload file
	_, err = megaSession.UploadFile(cachefile, n, name, nil)
	if err != nil {
		log.Print(err)
		return
	}
}

func mkpath(p []string, parent *mega.Node) (*mega.Node, error) {
	var n *mega.Node
	var err error

	root := megaSession.FS.GetRoot()
	// Root path is an empty array.
	if p[0] == "" {
		return root, nil
	}

	paths, err := megaSession.FS.PathLookup(root, p)
	// Path found
	if err == nil {
		// We only care about the last path.
		return paths[len(paths)-1], nil
	// Expected "not found" error, got something else
	} else if err.Error() != "Object (typically, node or user) not found" {
		return nil, err
	}

	l := len(p)
	if l == 1{
		n = parent
	} else {
		// if a/b/c then parent = mkpath(a/b)
		n, err = mkpath(p[:l-1], parent)
		if err != nil {
			return n, err
		}
	}
	return megaSession.CreateDir(p[l-1], n)
}

func node(url string) (*mega.Node, error) {
	trimmedPath := strings.Trim(url, "/")
	path := strings.Split(trimmedPath, "/")
	root := megaSession.FS.GetRoot()
	// Root path is an empty array.
	if path[0] == "" {
		return root, nil
	} else {
		paths, err := megaSession.FS.PathLookup(root, path)
		if err != nil {
			return nil, err
		}
		// We only care about the last path.
		return paths[len(paths)-1], nil
	}
}

func main() {
	user := os.Getenv("MEGA_USER")
	pass := os.Getenv("MEGA_PASSWD")
	megaSession = mega.New()
	if err := megaSession.Login(user, pass); err != nil {
		log.Fatal(err)
	}
	if err := os.Mkdir(CACHEDIR, 0700); err != nil && !os.IsExist(err) {
		log.Fatal(err)
	}
	http.HandleFunc("/", handle)
	log.Fatal(http.ListenAndServe("localhost:8080", nil))
}
<commit_msg>Add comments<commit_after>package main

import (
	"fmt"
	"github.com/t3rm1n4l/go-mega"
	"html"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
)

const CACHEDIR = "cache"
var megaSession *mega.Mega

func handle(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		node, err := node(r.URL.Path)
		if err != nil {
			if err.Error() == "Object (typically, node or user) not found" {
				w.WriteHeader(http.StatusNotFound)
				return
			}
			log.Print(err)
			return
		}
		switch node.GetType() {
		case mega.FOLDER, mega.ROOT:
			list(w, r, node)
		default:
			get(w, r, node)
		}

	case "PUT":
		put(w, r)
	}
}

func list(w http.ResponseWriter, r *http.Request, node *mega.Node) {
	children, err := megaSession.FS.GetChildren(node)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		log.Print(err)
		return
	}

	fmt.Fprint(w, "")
	fmt.Fprintf(w, "
%s
", html.EscapeString(r.URL.Path))
	fmt.Fprint(w, "
")
	if node != megaSession.FS.GetRoot() {
		fmt.Fprintf(w, "
..")
	}
	for _, child := range children {
		var folder string
		if child.GetType() == mega.FOLDER {
			folder = "/"
		}
		fmt.Fprintf(w, "
%s%s",
			html.EscapeString(child.GetName()), folder,
			html.EscapeString(child.GetName()), folder)
	}
	fmt.Fprint(w, "
")
}

func get(w http.ResponseWriter, r *http.Request, node *mega.Node) {
	// Cache files
	cachefile := CACHEDIR + r.URL.Path
	dir, _ := path.Split(cachefile)

	// Create local path
	if err := os.MkdirAll(dir, 0700); err != nil && !os.IsExist(err) {
		log.Print(err)
		return
	}
	// Do we have this cached?
	file, err := os.Open(cachefile)
	if err != nil && os.IsNotExist(err) {
		// Download file
		if err = megaSession.DownloadFile(node, cachefile, nil); err != nil {
			log.Print(err)
			w.WriteHeader(http.StatusInternalServerError)
			// Remove incomplete cachefile, in case one was created
			os.Remove(cachefile)
			return
		}

		// Open cached file
		file, err = os.Open(cachefile)
		if err != nil {
			log.Print(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
	}
	defer file.Close()
	_, err = io.Copy(w, file)
	if err != nil {
		log.Print(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func put(w http.ResponseWriter, r *http.Request) {
	cachefile := CACHEDIR + r.URL.Path
	dir, name := path.Split(cachefile)

	// Create local file
	if err := os.MkdirAll(dir, 0700); err != nil && !os.IsExist(err) {
		log.Print(err)
		return
	}
	fp, err := os.Create(cachefile)
	if err != nil {
		log.Print(err)
		return
	}
	_, err = io.Copy(fp, r.Body)
	if err != nil {
		log.Print(err)
		return
	}

	// Create Mega path
	dirarray := strings.Split(r.URL.Path, "/")
	root := megaSession.FS.GetRoot()
	n, err := mkpath(dirarray[1:len(dirarray)-1], root)
	if err != nil {
		log.Print(err)
		return
	}

	// Lookup Mega file (if it exists)
	paths, err := megaSession.FS.PathLookup(root, dirarray[1:])
	// Log unexpected errors
	if err != nil && err.Error() != "Object (typically, node or user) not found" {
		log.Print(err)
		return
	}
	// File exists, so delete it before uploading new file
	if err == nil {
		// We only care about the last node.
		lastnode := paths[len(paths)-1]
		// File exists, delete! (aka overwrite)
		if err = megaSession.Delete(lastnode, false); err != nil {
			log.Print(err)
			return
		}
	}
	// Finally, upload file
	_, err = megaSession.UploadFile(cachefile, n, name, nil)
	if err != nil {
		log.Print(err)
		return
	}
}

func mkpath(p []string, parent *mega.Node) (*mega.Node, error) {
	var n *mega.Node
	var err error

	root := megaSession.FS.GetRoot()
	// Root path is an empty array.
	if p[0] == "" {
		return root, nil
	}

	paths, err := megaSession.FS.PathLookup(root, p)
	// Path found
	if err == nil {
		// We only care about the last path.
		return paths[len(paths)-1], nil
	// Expected "not found" error, got something else
	} else if err.Error() != "Object (typically, node or user) not found" {
		return nil, err
	}

	l := len(p)
	if l == 1{
		n = parent
	} else {
		// if a/b/c then parent = mkpath(a/b)
		n, err = mkpath(p[:l-1], parent)
		if err != nil {
			return n, err
		}
	}
	return megaSession.CreateDir(p[l-1], n)
}

func node(url string) (*mega.Node, error) {
	trimmedPath := strings.Trim(url, "/")
	path := strings.Split(trimmedPath, "/")
	root := megaSession.FS.GetRoot()
	// Root path is an empty array.
	if path[0] == "" {
		return root, nil
	} else {
		paths, err := megaSession.FS.PathLookup(root, path)
		if err != nil {
			return nil, err
		}
		// We only care about the last path.
		return paths[len(paths)-1], nil
	}
}

func main() {
	user := os.Getenv("MEGA_USER")
	pass := os.Getenv("MEGA_PASSWD")
	megaSession = mega.New()
	if err := megaSession.Login(user, pass); err != nil {
		log.Fatal(err)
	}
	if err := os.Mkdir(CACHEDIR, 0700); err != nil && !os.IsExist(err) {
		log.Fatal(err)
	}
	http.HandleFunc("/", handle)
	log.Fatal(http.ListenAndServe("localhost:8080", nil))
}
<|endoftext|>


<commit_before>//Command to run test version:
//goapp serve app.yaml
//Command to deploy/update application:
//goapp deploy -application golangnode0 -version 0
//Command to test application without deploy
//goapp serve app.yaml

package main

import (
	"bytes"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"net/smtp"
)

var statusContent string = "Default status"

type webPage struct {
	Title string
}

type gmailUser struct {
	name string
	pswd string
}

func sendMail(msg string) {
	mailUser := gmailUser{
		"golangapplication@gmail.com",
		"",
	}
	auth := smtp.PlainAuth("",
		mailUser.name,
		mailUser.pswd,
		"smtp.gmail.com",
	)
	err := smtp.SendMail(
		"smtp.gmail.com:587",
		auth,
		mailUser.name,
		[]string{"rec@mail.com"},
		[]byte(msg),
	)
	if err != nil {
		log.Fatal(err)
	}
}

func echo(ID string) {
	url := "http://goappnode" + ID + ".appspot.com" + "/status"

	var jsonStr = []byte(`{"message":"Web echo"}`)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	statusContent = string(body)

}

func helloWorld(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello World!")
}

func startPage(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		templatePage, _ := template.ParseFiles("start.html")
		templatePage.Execute(w, &webPage{"simplePage"})
	case "POST":
		r.ParseForm()
		//go sendMail("Hello from test golang webapp!")
		//go sender()
		echo(1)
		fmt.Fprintf(w, "Successful read command/input from web-interface! Yeah! ")
	}
}

func statusServer(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		fmt.Fprintf(w, "Get status - "+statusContent)
	case "POST":
		r.ParseForm()
		fmt.Fprintf(w, "Get data by params in POST - OK")
		statusContent = "POST request - Responded"
	}
}

func showInfo(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Information page for test project.")
	fmt.Fprintln(w, "Language - Go;")
	fmt.Fprintln(w, "Platform - Google Application Engine;")
}

func init() {
	http.HandleFunc("/", startPage)
	http.HandleFunc("/helloworld", helloWorld)
	http.HandleFunc("/showinfo", showInfo)
	http.HandleFunc("/status", statusServer)

	//Wrong code for App Enine - server cant understand what it need to show
	//http.ListenAndServe(":80", nil)
}

//this func not needed for deploy on Google App Engine, init() func replace main()
/*
func main() {
	//fmt.Println("Hello, test server started on 8080 port.\n - /helloworld - show title page\n - /showinfo - show information about this thing")
	//http.ListenAndServe(":8080", nil)
	go sender()
}*/
<commit_msg>func fix<commit_after>//Command to run test version:
//goapp serve app.yaml
//Command to deploy/update application:
//goapp deploy -application golangnode0 -version 0
//Command to test application without deploy
//goapp serve app.yaml

package main

import (
	"bytes"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"net/smtp"
)

var statusContent string = "Default status"

type webPage struct {
	Title string
}

type gmailUser struct {
	name string
	pswd string
}

func sendMail(msg string) {
	mailUser := gmailUser{
		"golangapplication@gmail.com",
		"",
	}
	auth := smtp.PlainAuth("",
		mailUser.name,
		mailUser.pswd,
		"smtp.gmail.com",
	)
	err := smtp.SendMail(
		"smtp.gmail.com:587",
		auth,
		mailUser.name,
		[]string{"rec@mail.com"},
		[]byte(msg),
	)
	if err != nil {
		log.Fatal(err)
	}
}

func echo(ID int) {
	url := "http://goappnode" + ID + ".appspot.com" + "/status"

	var jsonStr = []byte(`{"message":"Web echo"}`)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	statusContent = string(body)

}

func helloWorld(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello World!")
}

func startPage(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		templatePage, _ := template.ParseFiles("start.html")
		templatePage.Execute(w, &webPage{"simplePage"})
	case "POST":
		r.ParseForm()
		//go sendMail("Hello from test golang webapp!")
		//go sender()
		echo(1)
		fmt.Fprintf(w, "Successful read command/input from web-interface! Yeah! ")
	}
}

func statusServer(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		fmt.Fprintf(w, "Get status - "+statusContent)
	case "POST":
		r.ParseForm()
		fmt.Fprintf(w, "Get data by params in POST - OK")
		statusContent = "POST request - Responded"
	}
}

func showInfo(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Information page for test project.")
	fmt.Fprintln(w, "Language - Go;")
	fmt.Fprintln(w, "Platform - Google Application Engine;")
}

func init() {
	http.HandleFunc("/", startPage)
	http.HandleFunc("/helloworld", helloWorld)
	http.HandleFunc("/showinfo", showInfo)
	http.HandleFunc("/status", statusServer)

	//Wrong code for App Enine - server cant understand what it need to show
	//http.ListenAndServe(":80", nil)
}

//this func not needed for deploy on Google App Engine, init() func replace main()
/*
func main() {
	//fmt.Println("Hello, test server started on 8080 port.\n - /helloworld - show title page\n - /showinfo - show information about this thing")
	//http.ListenAndServe(":8080", nil)
	go sender()
}*/
<|endoftext|>


<commit_before>package main

import (
	"io"
	"os"
	"os/exec"
)

func wrapStdin(proc *exec.Cmd, stdin io.Reader, inFlag bool, done chan bool) int {
	if !inFlag {
		return 0
	}

	/*fmt.Fprintf(os.Stderr, "Wrapping stdin")*/

	pipe, err := proc.StdinPipe()
	fatal_if(err)

	go inLoop(pipe, stdin, done)
	return 1
}

func wrapStdout(proc *exec.Cmd, outstream io.Writer, opt byte, done chan bool) int {
	pipe, err := proc.StdoutPipe()
	fatal_if(err)

	/*fmt.Fprintf(os.Stderr, "Wrapping stdout with %v\n", opt)*/

	go outLoop(pipe, outstream, opt, done)
	return 1
}

func wrapStderr(proc *exec.Cmd, outstream io.Writer, opt byte, done chan bool) int {
	pipe, err := proc.StderrPipe()
	fatal_if(err)

	/*fmt.Fprintf(os.Stderr, "Wrapping stderr with %v\n", opt)*/

	go outLoop(pipe, outstream, opt, done)
	return 1
}

///

func inLoop(pipe io.WriteCloser, stdin io.Reader, done chan bool) {
	buf := make([]byte, 2)
	logger.Println("Entering stdin loop")
	for {
		bytes_read, read_err := io.ReadFull(stdin, buf)
		if read_err == io.EOF && bytes_read == 0 {
			pipe.Close()
			break
		}
		fatal_if(read_err)

		length := read16_be(buf)
		logger.Printf("in: packet length = %v\n", length)
		if length == 0 {
			// this is how Porcelain signals EOF from Elixir
			pipe.Close()
			break
		}

		bytes_written, write_err := io.CopyN(pipe, stdin, int64(length))
		logger.Printf("in: copied %v bytes\n", bytes_written)
		fatal_if(write_err)
	}
	done <- true
}

///

// Maximum buffer size for protocol 0.0 is 2 + 2^16-1 - 1
//
//   * 2 is the packet length
//   * 2^16-1 is the maximum amount of data that can be encoded in a
//     2-byte-length packet
//   * 1 byte is used for framing, so it has to be included in the total length
//
const kOutputBufferSize = 1<<16

func outLoop(pipe io.ReadCloser, outstream io.Writer, char byte, done chan bool) {
	buf := make([]byte, kOutputBufferSize)
	buf[2] = char
	logger.Printf("Entering out loop with %v\n", char)
	for {
		bytes_read, read_err := pipe.Read(buf[3:])
		logger.Printf("out: read bytes: %v\n", bytes_read)
		if bytes_read > 0 {
			write16_be(buf[:2], bytes_read+1)
			bytes_written, write_err := outstream.Write(buf[:2+bytes_read+1])
			logger.Printf("out: written bytes: %v\n", bytes_written)
			fatal_if(write_err)
		}
		if read_err == io.EOF /*|| bytes_read == 0*/ {
			// !!!
			// The note below is currently irrelevant, but left here in case
			// the bug reappers in the future.
			// !!!

			// From io.Reader docs:
			//
			//   Implementations of Read are discouraged from returning a zero
			//   byte count with a nil error, and callers should treat that
			//   situation as a no-op.
			//
			// In this case it appears that 0 bytes may be returned
			// indefinitely when reading from stderr. Therefore we close the pipe.
			if read_err == io.EOF {
				logger.Println("Encountered EOF on input")
			} else {
				logger.Println("Read 0 bytes with no error")
			}
			break
		}
		if read_err != nil {
			switch read_err.(type) {
			case *os.PathError:
				// known error
				break
			default:
				fatal(read_err)
			}
		}
	}
	pipe.Close()
	done <- true
}

// Unpack the 2-byte integer stored in big endian order
func read16_be(data []byte) int16 {
	return int16(data[0]) << 8 | int16(data[1])
}

// Pack a 2-byte integer in big endian order
func write16_be(data []byte, num int) {
	data[0] = byte(num >> 8)
	data[1] = byte(num)
}
<commit_msg>Make the output buffer static<commit_after>package main

import (
	"io"
	"os"
	"os/exec"
)

func wrapStdin(proc *exec.Cmd, stdin io.Reader, inFlag bool, done chan bool) int {
	if !inFlag {
		return 0
	}

	/*fmt.Fprintf(os.Stderr, "Wrapping stdin")*/

	pipe, err := proc.StdinPipe()
	fatal_if(err)

	go inLoop(pipe, stdin, done)
	return 1
}

func wrapStdout(proc *exec.Cmd, outstream io.Writer, opt byte, done chan bool) int {
	pipe, err := proc.StdoutPipe()
	fatal_if(err)

	/*fmt.Fprintf(os.Stderr, "Wrapping stdout with %v\n", opt)*/

	go outLoop(pipe, outstream, opt, done)
	return 1
}

func wrapStderr(proc *exec.Cmd, outstream io.Writer, opt byte, done chan bool) int {
	pipe, err := proc.StderrPipe()
	fatal_if(err)

	/*fmt.Fprintf(os.Stderr, "Wrapping stderr with %v\n", opt)*/

	go outLoop(pipe, outstream, opt, done)
	return 1
}

///

func inLoop(pipe io.WriteCloser, stdin io.Reader, done chan bool) {
	buf := make([]byte, 2)
	logger.Println("Entering stdin loop")
	for {
		bytes_read, read_err := io.ReadFull(stdin, buf)
		if read_err == io.EOF && bytes_read == 0 {
			pipe.Close()
			break
		}
		fatal_if(read_err)

		length := read16_be(buf)
		logger.Printf("in: packet length = %v\n", length)
		if length == 0 {
			// this is how Porcelain signals EOF from Elixir
			pipe.Close()
			break
		}

		bytes_written, write_err := io.CopyN(pipe, stdin, int64(length))
		logger.Printf("in: copied %v bytes\n", bytes_written)
		fatal_if(write_err)
	}
	done <- true
}

///

// Maximum buffer size for protocol 0.0 is 2 + 2^16-1 - 1
//
//   * 2 is the packet length
//   * 2^16-1 is the maximum amount of data that can be encoded in a
//     2-byte-length packet
//   * 1 byte is used for framing, so it has to be included in the total length
//
const kOutputBufferSize = 1<<16
var outBuf [kOutputBufferSize]byte

func outLoop(pipe io.ReadCloser, outstream io.Writer, char byte, done chan bool) {
	buf := outBuf
	buf[2] = char
	logger.Printf("Entering out loop with %v\n", char)
	for {
		bytes_read, read_err := pipe.Read(buf[3:])
		logger.Printf("out: read bytes: %v\n", bytes_read)
		if bytes_read > 0 {
			write16_be(buf[:2], bytes_read+1)
			bytes_written, write_err := outstream.Write(buf[:2+bytes_read+1])
			logger.Printf("out: written bytes: %v\n", bytes_written)
			fatal_if(write_err)
		}
		if read_err == io.EOF /*|| bytes_read == 0*/ {
			// !!!
			// The note below is currently irrelevant, but left here in case
			// the bug reappers in the future.
			// !!!

			// From io.Reader docs:
			//
			//   Implementations of Read are discouraged from returning a zero
			//   byte count with a nil error, and callers should treat that
			//   situation as a no-op.
			//
			// In this case it appears that 0 bytes may be returned
			// indefinitely when reading from stderr. Therefore we close the pipe.
			if read_err == io.EOF {
				logger.Println("Encountered EOF on input")
			} else {
				logger.Println("Read 0 bytes with no error")
			}
			break
		}
		if read_err != nil {
			switch read_err.(type) {
			case *os.PathError:
				// known error
				break
			default:
				fatal(read_err)
			}
		}
	}
	pipe.Close()
	done <- true
}

// Unpack the 2-byte integer stored in big endian order
func read16_be(data []byte) int16 {
	return int16(data[0]) << 8 | int16(data[1])
}

// Pack a 2-byte integer in big endian order
func write16_be(data []byte, num int) {
	data[0] = byte(num >> 8)
	data[1] = byte(num)
}
<|endoftext|>