package main import ( "fmt" "log" "os" "time" "golang.org/x/sys/execabs" ) // ErrorSpec specifies the error we care about. type ErrorSpec struct { // Errno is the error name as an errno value (e.g., ECONNREFUSED). Errno string // Failure is the error name according to OONI (e.g., FailureConnectionRefused). Failure string } // TODO(kelmenhorst): find out if we need more system errors here. Currently // the code does not generate any mapping if Failure is empty. // Specs contains all the error specs. var Specs = []ErrorSpec{{ Errno: "ECANCELED", Failure: "Interrupted", }, { Errno: "ECONNREFUSED", Failure: "ConnectionRefused", }, { Errno: "ECONNRESET", Failure: "ConnectionReset", }, { Errno: "EHOSTUNREACH", Failure: "HostUnreachable", }, { Errno: "ETIMEDOUT", Failure: "GenericTimeoutError", }, { Errno: "EAFNOSUPPORT", }, { Errno: "EADDRINUSE", }, { Errno: "EADDRNOTAVAIL", }, { Errno: "EISCONN", }, { Errno: "EFAULT", }, { Errno: "EBADF", }, { Errno: "ECONNABORTED", }, { Errno: "EALREADY", }, { Errno: "EDESTADDRREQ", }, { Errno: "EINTR", }, { Errno: "EINVAL", }, { Errno: "EMSGSIZE", }, { Errno: "ENETDOWN", }, { Errno: "ENETRESET", }, { Errno: "ENETUNREACH", }, { Errno: "ENOBUFS", }, { Errno: "ENOPROTOOPT", }, { Errno: "ENOTSOCK", }, { Errno: "ENOTCONN", }, { Errno: "EWOULDBLOCK", }, { Errno: "EACCES", }, { Errno: "EPROTONOSUPPORT", }, { Errno: "EPROTOTYPE", }} func fileCreate(filename string) *os.File { filep, err := os.Create(filename) if err != nil { log.Fatal(err) } return filep } func fileWrite(filep *os.File, content string) { if _, err := filep.WriteString(content); err != nil { log.Fatal(err) } } func fileClose(filep *os.File) { if err := filep.Close(); err != nil { log.Fatal(err) } } func filePrintf(filep *os.File, format string, v ...interface{}) { fileWrite(filep, fmt.Sprintf(format, v...)) } func gofmt(filename string) { cmd := execabs.Command("go", "fmt", filename) if err := cmd.Run(); err != nil { log.Fatal(err) } } func writeSystemSpecificFile(kind string) { filename := "errno_" + kind + ".go" filep := fileCreate(filename) fileWrite(filep, "// Code generated by go generate; DO NOT EDIT.\n") filePrintf(filep, "// Generated: %+v\n\n", time.Now()) fileWrite(filep, "package errorsx\n\n") filePrintf(filep, "import \"golang.org/x/sys/%s\"\n\n", kind) fileWrite(filep, "const (\n") for _, spec := range Specs { filePrintf(filep, "\t%s = %s.%s\n", spec.Errno, kind, spec.Errno) } fileWrite(filep, ")\n\n") fileClose(filep) gofmt(filename) } func writeGenericFile() { filename := "errno.go" filep := fileCreate(filename) fileWrite(filep, "// Code generated by go generate; DO NOT EDIT.\n") filePrintf(filep, "// Generated: %+v\n\n", time.Now()) fileWrite(filep, "package errorsx\n\n") fileWrite(filep, "//go:generate go run ./generator/\n\n") fileWrite(filep, "import (\n") fileWrite(filep, "\t\"errors\"\n") fileWrite(filep, "\t\"syscall\"\n") fileWrite(filep, ")\n\n") fileWrite(filep, "// toSyscallErr converts a syscall error to the\n") fileWrite(filep, "// proper OONI error. Returns the OONI error string\n") fileWrite(filep, "// on success, an empty string otherwise.\n") fileWrite(filep, "func toSyscallErr(err error) string {\n") fileWrite(filep, "\t// filter out system errors: necessary to detect all windows errors\n") fileWrite(filep, "\t// https://github.com/ooni/probe/issues/1526 describes the problem\n") fileWrite(filep, "\t// of mapping localized windows errors.\n") fileWrite(filep, "\tvar errno syscall.Errno\n") fileWrite(filep, "\tif !errors.As(err, &errno) {\n") fileWrite(filep, "\t\treturn \"\"\n") fileWrite(filep, "\t}\n") fileWrite(filep, "\tswitch errno {\n") for _, spec := range Specs { if spec.Failure != "" { filePrintf(filep, "\tcase %s:\n", spec.Errno) filePrintf(filep, "\t\treturn Failure%s\n", spec.Failure) } } fileWrite(filep, "\t}\n") fileWrite(filep, "\treturn \"\"\n") fileWrite(filep, "}\n\n") fileClose(filep) gofmt(filename) } func main() { writeSystemSpecificFile("unix") writeSystemSpecificFile("windows") writeGenericFile() }