<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"unsafe"

	"github.com/gorilla/mux"

	"github.com/lxc/lxd/shared"
)

func socketPath() string {
	return shared.VarPath("devlxd")
}

type devLxdResponse struct {
	content interface{}
	code    int
	ctype   string
}

func okResponse(ct interface{}, ctype string) *devLxdResponse {
	return &devLxdResponse{ct, http.StatusOK, ctype}
}

type devLxdHandler struct {
	path string

	/*
	 * This API will have to be changed slightly when we decide to support
	 * websocket events upgrading, but since we don't have events on the
	 * server side right now either, I went the simple route to avoid
	 * needless noise.
	 */
	f func(c container, r *http.Request) *devLxdResponse
}

var configGet = devLxdHandler{"/1.0/config", func(c container, r *http.Request) *devLxdResponse {
	filtered := []string{}
	for k, _ := range c.ConfigGet() {
		if strings.HasPrefix(k, "user.") {
			filtered = append(filtered, fmt.Sprintf("/1.0/config/%s", k))
		}
	}
	return okResponse(filtered, "json")
}}

var configKeyGet = devLxdHandler{"/1.0/config/{key}", func(c container, r *http.Request) *devLxdResponse {
	key := mux.Vars(r)["key"]
	if !strings.HasPrefix(key, "user.") {
		return &devLxdResponse{"not authorized", http.StatusForbidden, "raw"}
	}

	value, ok := c.ConfigGet()[key]
	if !ok {
		return &devLxdResponse{"not found", http.StatusNotFound, "raw"}
	}

	return okResponse(value, "raw")
}}

var metadataGet = devLxdHandler{"/1.0/meta-data", func(c container, r *http.Request) *devLxdResponse {
	value := c.ConfigGet()["user.meta-data"]
	return okResponse(fmt.Sprintf("#cloud-config\ninstance-id: %s\nlocal-hostname: %s\n%s", c.NameGet(), c.NameGet(), value), "raw")
}}

var handlers = []devLxdHandler{
	devLxdHandler{"/", func(c container, r *http.Request) *devLxdResponse {
		return okResponse([]string{"/1.0"}, "json")
	}},
	devLxdHandler{"/1.0", func(c container, r *http.Request) *devLxdResponse {
		return okResponse(shared.Jmap{"api_compat": 0}, "json")
	}},
	configGet,
	configKeyGet,
	metadataGet,
	/* TODO: events */
}

func hoistReq(f func(container, *http.Request) *devLxdResponse, d *Daemon) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		conn := extractUnderlyingConn(w)
		pid, ok := pidMapper.m[conn]
		if !ok {
			http.Error(w, pidNotInContainerErr.Error(), 500)
			return
		}

		c, err := findContainerForPid(pid, d)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}

		resp := f(c, r)
		if resp.code != http.StatusOK {
			w.Header().Set("Content-Type", "application/octet-stream")
			http.Error(w, fmt.Sprintf("%s", resp.content), resp.code)
		} else if resp.ctype == "json" {
			w.Header().Set("Content-Type", "application/json")
			WriteJSON(w, resp.content)
		} else {
			w.Header().Set("Content-Type", "application/octet-stream")
			fmt.Fprintf(w, resp.content.(string))
		}
	}
}

func createAndBindDevLxd() (*net.UnixListener, error) {
	if err := os.MkdirAll(socketPath(), 0777); err != nil {
		return nil, err
	}

	sockFile := path.Join(socketPath(), "sock")

	/*
	 * If this socket exists, that means a previous lxd died and didn't
	 * clean up after itself. We assume that the LXD is actually dead if we
	 * get this far, since StartDaemon() tries to connect to the actual lxd
	 * socket to make sure that it is actually dead. So, it is safe to
	 * remove it here without any checks.
	 *
	 * Also, it would be nice to SO_REUSEADDR here so we don't have to
	 * delete the socket, but we can't:
	 *   http://stackoverflow.com/questions/15716302/so-reuseaddr-and-af-unix
	 *
	 * Note that this will force clients to reconnect when LXD is restarted.
	 */
	if err := os.Remove(sockFile); err != nil && !os.IsNotExist(err) {
		return nil, err
	}

	unixAddr, err := net.ResolveUnixAddr("unix", sockFile)
	if err != nil {
		return nil, err
	}

	unixl, err := net.ListenUnix("unix", unixAddr)
	if err != nil {
		return nil, err
	}

	if err := os.Chmod(sockFile, 0666); err != nil {
		return nil, err
	}

	return unixl, nil
}

func setupDevLxdMount(c *containerLXD) error {
	mtab := fmt.Sprintf("%s dev/lxd none bind,create=dir 0 0", socketPath())
	return setConfigItem(c, "lxc.mount.entry", mtab)
}

func devLxdServer(d *Daemon) http.Server {
	m := mux.NewRouter()

	for _, handler := range handlers {
		m.HandleFunc(handler.path, hoistReq(handler.f, d))
	}

	return http.Server{
		Handler:   m,
		ConnState: pidMapper.ConnStateHandler,
	}
}

/*<commit_msg>don't pass lock by value<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"unsafe"

	"github.com/gorilla/mux"

	"github.com/lxc/lxd/shared"
)

func socketPath() string {
	return shared.VarPath("devlxd")
}

type devLxdResponse struct {
	content interface{}
	code    int
	ctype   string
}

func okResponse(ct interface{}, ctype string) *devLxdResponse {
	return &devLxdResponse{ct, http.StatusOK, ctype}
}

type devLxdHandler struct {
	path string

	/*
	 * This API will have to be changed slightly when we decide to support
	 * websocket events upgrading, but since we don't have events on the
	 * server side right now either, I went the simple route to avoid
	 * needless noise.
	 */
	f func(c container, r *http.Request) *devLxdResponse
}

var configGet = devLxdHandler{"/1.0/config", func(c container, r *http.Request) *devLxdResponse {
	filtered := []string{}
	for k, _ := range c.ConfigGet() {
		if strings.HasPrefix(k, "user.") {
			filtered = append(filtered, fmt.Sprintf("/1.0/config/%s", k))
		}
	}
	return okResponse(filtered, "json")
}}

var configKeyGet = devLxdHandler{"/1.0/config/{key}", func(c container, r *http.Request) *devLxdResponse {
	key := mux.Vars(r)["key"]
	if !strings.HasPrefix(key, "user.") {
		return &devLxdResponse{"not authorized", http.StatusForbidden, "raw"}
	}

	value, ok := c.ConfigGet()[key]
	if !ok {
		return &devLxdResponse{"not found", http.StatusNotFound, "raw"}
	}

	return okResponse(value, "raw")
}}

var metadataGet = devLxdHandler{"/1.0/meta-data", func(c container, r *http.Request) *devLxdResponse {
	value := c.ConfigGet()["user.meta-data"]
	return okResponse(fmt.Sprintf("#cloud-config\ninstance-id: %s\nlocal-hostname: %s\n%s", c.NameGet(), c.NameGet(), value), "raw")
}}

var handlers = []devLxdHandler{
	devLxdHandler{"/", func(c container, r *http.Request) *devLxdResponse {
		return okResponse([]string{"/1.0"}, "json")
	}},
	devLxdHandler{"/1.0", func(c container, r *http.Request) *devLxdResponse {
		return okResponse(shared.Jmap{"api_compat": 0}, "json")
	}},
	configGet,
	configKeyGet,
	metadataGet,
	/* TODO: events */
}

func hoistReq(f func(container, *http.Request) *devLxdResponse, d *Daemon) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		conn := extractUnderlyingConn(w)
		pid, ok := pidMapper.m[conn]
		if !ok {
			http.Error(w, pidNotInContainerErr.Error(), 500)
			return
		}

		c, err := findContainerForPid(pid, d)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}

		resp := f(c, r)
		if resp.code != http.StatusOK {
			w.Header().Set("Content-Type", "application/octet-stream")
			http.Error(w, fmt.Sprintf("%s", resp.content), resp.code)
		} else if resp.ctype == "json" {
			w.Header().Set("Content-Type", "application/json")
			WriteJSON(w, resp.content)
		} else {
			w.Header().Set("Content-Type", "application/octet-stream")
			fmt.Fprintf(w, resp.content.(string))
		}
	}
}

func createAndBindDevLxd() (*net.UnixListener, error) {
	if err := os.MkdirAll(socketPath(), 0777); err != nil {
		return nil, err
	}

	sockFile := path.Join(socketPath(), "sock")

	/*
	 * If this socket exists, that means a previous lxd died and didn't
	 * clean up after itself. We assume that the LXD is actually dead if we
	 * get this far, since StartDaemon() tries to connect to the actual lxd
	 * socket to make sure that it is actually dead. So, it is safe to
	 * remove it here without any checks.
	 *
	 * Also, it would be nice to SO_REUSEADDR here so we don't have to
	 * delete the socket, but we can't:
	 *   http://stackoverflow.com/questions/15716302/so-reuseaddr-and-af-unix
	 *
	 * Note that this will force clients to reconnect when LXD is restarted.
	 */
	if err := os.Remove(sockFile); err != nil && !os.IsNotExist(err) {
		return nil, err
	}

	unixAddr, err := net.ResolveUnixAddr("unix", sockFile)
	if err != nil {
		return nil, err
	}

	unixl, err := net.ListenUnix("unix", unixAddr)
	if err != nil {
		return nil, err
	}

	if err := os.Chmod(sockFile, 0666); err != nil {
		return nil, err
	}

	return unixl, nil
}

func setupDevLxdMount(c *containerLXD) error {
	mtab := fmt.Sprintf("%s dev/lxd none bind,create=dir 0 0", socketPath())
	return setConfigItem(c, "lxc.mount.entry", mtab)
}

func devLxdServer(d *Daemon) *http.Server {
	m := mux.NewRouter()

	for _, handler := range handlers {
		m.HandleFunc(handler.path, hoistReq(handler.f, d))
	}

	return &http.Server{
		Handler:   m,
		ConnState: pidMapper.ConnStateHandler,
	}
}

/*
{'fraction_non_alphanumeric': 0.15270935960591134, 'fraction_numerical': 0.008100711548987411, 'mean_word_length': 4.017023613399231, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, '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': '4762244', 'n_tokens_mistral': 3507, 'n_tokens_neox': 3283, 'n_words': 868}