<commit_before>package daemon

import (
	"bufio"
	"context"
	"encoding/json"
	"net"
	"os"
	"path/filepath"
	"time"

	"crawshaw.io/sqlite"
	"github.com/itchio/butler/butlerd"
	"github.com/itchio/butler/database"
	"github.com/sourcegraph/jsonrpc2"

	"github.com/itchio/butler/comm"
	"github.com/itchio/butler/mansion"
	"github.com/pkg/errors"
)

func Register(ctx *mansion.Context) {
	cmd := ctx.App.Command("daemon", "Start a butlerd instance").Hidden()
	ctx.Register(cmd, do)
}

const minSecretLength = 256

func do(ctx *mansion.Context) {
	if !comm.JsonEnabled() {
		comm.Notice("Hello from butler daemon", []string{"We can't do anything interesting without --json, bailing out", "", "Learn more: https://docs.itch.ovh/butlerd/master/"})
		os.Exit(1)
	}

	if ctx.DBPath == "" {
		comm.Dief("butlerd: dbPath must be set")
	}

	comm.Object("butlerd/secret-request", map[string]interface{}{
		"minLength": minSecretLength,
	})

	secretChan := make(chan string)
	go func() {
		secret := ""
		scanner := bufio.NewScanner(os.Stdin)
		for scanner.Scan() {
			line := scanner.Bytes()
			m := make(map[string]interface{})
			err := json.Unmarshal(line, &m)
			if err != nil {
				comm.Warnf("could not unmarshal JSON input, ignoring: %s", err.Error())
				continue
			}

			if typ, ok := m["type"].(string); ok {
				switch typ {
				case "butlerd/secret-result":
					if s, ok := m["secret"].(string); ok {
						secret = s
						secretChan <- secret
						comm.Logf("Received secret")
						return
					}
				default:
					comm.Warnf("unrecognized json message type %s, ignoring", typ)
				}
			} else {
				comm.Warnf("json message missing 'type' field, ignoring")
			}
		}
	}()

	var secret string
	select {
	case secret = <-secretChan:
		// woo
	case <-time.After(15 * time.Second):
		comm.Dief("butlerd: Timed out while waiting for secret")
	}

	if len(secret) < minSecretLength {
		comm.Dief("butlerd: Secret too short (must be %d chars, received %d chars) or more", minSecretLength, len(secret))
	}

	err := os.MkdirAll(filepath.Dir(ctx.DBPath), 0755)
	if err != nil {
		ctx.Must(errors.WithMessage(err, "creating DB directory if necessary"))
	}

	dbPool, err := sqlite.Open(ctx.DBPath, 0, 2)
	if err != nil {
		ctx.Must(errors.WithMessage(err, "opening DB for the first time"))
	}
	defer dbPool.Close()

	func() {
		conn := dbPool.Get(context.Background().Done())
		defer dbPool.Put(conn)
		err = database.Prepare(conn)
	}()
	if err != nil {
		ctx.Must(errors.WithMessage(err, "preparing DB"))
	}

	ctx.Must(Do(ctx, context.Background(), dbPool, secret, func(addr string) {
		comm.Object("butlerd/listen-notification", map[string]interface{}{<commit_msg>Bump sqlite connection pool to 100<commit_after>package daemon

import (
	"bufio"
	"context"
	"encoding/json"
	"net"
	"os"
	"path/filepath"
	"time"

	"crawshaw.io/sqlite"
	"github.com/itchio/butler/butlerd"
	"github.com/itchio/butler/database"
	"github.com/sourcegraph/jsonrpc2"

	"github.com/itchio/butler/comm"
	"github.com/itchio/butler/mansion"
	"github.com/pkg/errors"
)

func Register(ctx *mansion.Context) {
	cmd := ctx.App.Command("daemon", "Start a butlerd instance").Hidden()
	ctx.Register(cmd, do)
}

const minSecretLength = 256

func do(ctx *mansion.Context) {
	if !comm.JsonEnabled() {
		comm.Notice("Hello from butler daemon", []string{"We can't do anything interesting without --json, bailing out", "", "Learn more: https://docs.itch.ovh/butlerd/master/"})
		os.Exit(1)
	}

	if ctx.DBPath == "" {
		comm.Dief("butlerd: dbPath must be set")
	}

	comm.Object("butlerd/secret-request", map[string]interface{}{
		"minLength": minSecretLength,
	})

	secretChan := make(chan string)
	go func() {
		secret := ""
		scanner := bufio.NewScanner(os.Stdin)
		for scanner.Scan() {
			line := scanner.Bytes()
			m := make(map[string]interface{})
			err := json.Unmarshal(line, &m)
			if err != nil {
				comm.Warnf("could not unmarshal JSON input, ignoring: %s", err.Error())
				continue
			}

			if typ, ok := m["type"].(string); ok {
				switch typ {
				case "butlerd/secret-result":
					if s, ok := m["secret"].(string); ok {
						secret = s
						secretChan <- secret
						comm.Logf("Received secret")
						return
					}
				default:
					comm.Warnf("unrecognized json message type %s, ignoring", typ)
				}
			} else {
				comm.Warnf("json message missing 'type' field, ignoring")
			}
		}
	}()

	var secret string
	select {
	case secret = <-secretChan:
		// woo
	case <-time.After(15 * time.Second):
		comm.Dief("butlerd: Timed out while waiting for secret")
	}

	if len(secret) < minSecretLength {
		comm.Dief("butlerd: Secret too short (must be %d chars, received %d chars) or more", minSecretLength, len(secret))
	}

	err := os.MkdirAll(filepath.Dir(ctx.DBPath), 0755)
	if err != nil {
		ctx.Must(errors.WithMessage(err, "creating DB directory if necessary"))
	}

	dbPool, err := sqlite.Open(ctx.DBPath, 0, 100)
	if err != nil {
		ctx.Must(errors.WithMessage(err, "opening DB for the first time"))
	}
	defer dbPool.Close()

	func() {
		conn := dbPool.Get(context.Background().Done())
		defer dbPool.Put(conn)
		err = database.Prepare(conn)
	}()
	if err != nil {
		ctx.Must(errors.WithMessage(err, "preparing DB"))
	}

	ctx.Must(Do(ctx, context.Background(), dbPool, secret, func(addr string) {
		comm.Object("butlerd/listen-notification", map[string]interface{}{
{'fraction_non_alphanumeric': 0.16238514907181698, 'fraction_numerical': 0.005812863303956498, 'mean_word_length': 3.7371225577264653, 'pattern_counts': {'":': 4, '<': 11, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3264915', 'n_tokens_mistral': 2179, 'n_tokens_neox': 1920, 'n_words': 434}