diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f6ba1d7..f08cd93 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,7 +8,3 @@ updates: directory: / schedule: interval: weekly - - package-ecosystem: gomod - directory: /serial - schedule: - interval: daily diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 0c9165c..7a708f6 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -11,4 +11,4 @@ jobs: - name: Run ShellCheck uses: ludeeus/action-shellcheck@master env: - SHELLCHECK_OPTS: -x -e SC2001 -e SC2002 -e SC2223 -e SC2034 -e SC2064 + SHELLCHECK_OPTS: -x -e SC2001 -e SC2002 -e SC2223 -e SC2034 -e SC2064 diff --git a/.github/workflows/hub.yml b/.github/workflows/hub.yml index 216d6a1..b5f2dac 100644 --- a/.github/workflows/hub.yml +++ b/.github/workflows/hub.yml @@ -5,6 +5,7 @@ on: - master paths: - readme.md + - README.md - .github/workflows/hub.yml jobs: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 20e437b..288e420 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,11 +4,15 @@ on: - master paths: - '**/*.sh' - + - '.github/workflows/test.yml' + - '.github/workflows/check.yml' + pull_request: paths: - '**/*.sh' - + - '.github/workflows/test.yml' + - '.github/workflows/check.yml' + name: "Test" permissions: {} diff --git a/Dockerfile b/Dockerfile index 7d38aae..f4dfb8e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,10 @@ -FROM golang AS builder - -COPY serial/ /src/serial/ -WORKDIR /src/serial +FROM golang as builder +WORKDIR / +RUN git clone https://github.com/qemu-tools/qemu-host.git +WORKDIR /qemu-host/src RUN go mod download -RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /src/serial/main . +RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /qemu-host/src/main . FROM debian:bookworm-slim @@ -34,12 +34,12 @@ RUN apt-get update && apt-get -y upgrade && \ COPY run/*.sh /run/ COPY agent/*.sh /agent/ -COPY --from=builder /src/serial/main /run/serial.bin +COPY --from=builder /qemu-host/src/main /run/host.bin RUN ["chmod", "+x", "/run/run.sh"] RUN ["chmod", "+x", "/run/check.sh"] RUN ["chmod", "+x", "/run/server.sh"] -RUN ["chmod", "+x", "/run/serial.bin"] +RUN ["chmod", "+x", "/run/host.bin"] VOLUME /storage @@ -62,8 +62,8 @@ ENV VERSION=$VERSION_ARG LABEL org.opencontainers.image.created=${DATE_ARG} LABEL org.opencontainers.image.revision=${BUILD_ARG} LABEL org.opencontainers.image.version=${VERSION_ARG} -LABEL org.opencontainers.image.url=https://hub.docker.com/r/kroese/virtual-dsm/ LABEL org.opencontainers.image.source=https://github.com/kroese/virtual-dsm/ +LABEL org.opencontainers.image.url=https://hub.docker.com/r/kroese/virtual-dsm/ HEALTHCHECK --interval=30s --retries=1 CMD /run/check.sh diff --git a/readme.md b/readme.md index 79d179b..3d4a49c 100644 --- a/readme.md +++ b/readme.md @@ -182,4 +182,4 @@ Based on an [article](https://jxcn.org/2022/04/vdsm-first-try/) by JXCN. ## Disclaimer -Only run this container on Synology hardware, any other use is not permitted and might not be legal according to their terms. +Only run this container on Synology hardware, any other use is not permitted and might not be legal. diff --git a/run/run.sh b/run/run.sh index 3d02df2..d71c5f9 100755 --- a/run/run.sh +++ b/run/run.sh @@ -81,5 +81,5 @@ set +m if (( KERNEL > 4 )); then pidwait -F "${_QEMU_PID}" & wait $! else - tail --pid "$(cat ${_QEMU_PID})" --follow /dev/null & wait $! + tail --pid "$(cat "${_QEMU_PID}")" --follow /dev/null & wait $! fi diff --git a/run/serial.sh b/run/serial.sh index 938bac3..13dd2c3 100644 --- a/run/serial.sh +++ b/run/serial.sh @@ -4,12 +4,11 @@ set -eu # Docker environment variables : ${HOST_CPU:=''} -: ${HOST_BUILD:='42962'} -: ${HOST_VERSION:='2.6.1-12139'} -: ${HOST_TIMESTAMP:='1679863686'} -: ${HOST_SERIAL:='0000000000000'} -: ${GUEST_SERIAL:='0000000000000'} -: ${GUEST_UUID:='ba13a19a-c0c1-4fef-9346-915ed3b98341'} +: ${HOST_BUILD:=''} +: ${HOST_SERIAL:=''} +: ${GUEST_SERIAL:=''} +: ${HOST_VERSION:=''} +: ${HOST_TIMESTAMP:=''} if [ -z "$HOST_CPU" ]; then HOST_CPU=$(lscpu | sed -nr '/Model name/ s/.*:\s*(.*) @ .*/\1/p' | sed ':a;s/ / /;ta' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g') @@ -21,14 +20,24 @@ else HOST_CPU="QEMU, Virtual CPU, X86_64" fi -./run/serial.bin -cpu="${CPU_CORES}" \ - -cpu_arch="${HOST_CPU}" \ - -hostsn="${HOST_SERIAL}" \ - -guestsn="${GUEST_SERIAL}" \ - -vmmts="${HOST_TIMESTAMP}" \ - -vmmversion="${HOST_VERSION}" \ - -buildnumber="${HOST_BUILD}" \ - -guestuuid="${GUEST_UUID}" > /dev/null 2>&1 & +HOST_ARGS=() +HOST_ARGS+=("-cpu_arch=${HOST_CPU}") + +[ -n "$CPU_CORES" ] && HOST_ARGS+=("-cpu=${CPU_CORES}") +[ -n "$HOST_BUILD" ] && HOST_ARGS+=("-build=${HOST_BUILD}") +[ -n "$HOST_SERIAL" ] && HOST_ARGS+=("-hostsn=${HOST_SERIAL}") +[ -n "$HOST_TIMESTAMP" ] && HOST_ARGS+=("-ts=${HOST_TIMESTAMP}") +[ -n "$GUEST_SERIAL" ] && HOST_ARGS+=("-guestsn=${GUEST_SERIAL}") +[ -n "$HOST_VERSION" ] && HOST_ARGS+=("-version=${HOST_VERSION}") + +if [ "$DEBUG" = "Y" ]; then + echo -n "./run/host.bin " + echo "${HOST_ARGS[*]}" && echo +fi + +./run/host.bin "${HOST_ARGS[@]}" > /dev/null 2>&1 & + +# Configure serial ports SERIAL_OPTS="\ -serial mon:stdio \ diff --git a/serial/doc.go b/serial/doc.go deleted file mode 100644 index 06ab7d0..0000000 --- a/serial/doc.go +++ /dev/null @@ -1 +0,0 @@ -package main diff --git a/serial/go.mod b/serial/go.mod deleted file mode 100644 index c859227..0000000 --- a/serial/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module vdsm-serial - -go 1.20 - -require github.com/gorilla/mux v1.8.0 diff --git a/serial/go.sum b/serial/go.sum deleted file mode 100644 index 5350288..0000000 --- a/serial/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= diff --git a/serial/main.go b/serial/main.go deleted file mode 100644 index 8c1311c..0000000 --- a/serial/main.go +++ /dev/null @@ -1,263 +0,0 @@ -package main - -import ( - "fmt" - "log" - "net" - "flag" - "bytes" - "strconv" - "net/http" - "math/rand" - "encoding/binary" - "github.com/gorilla/mux" -) - -type REQ struct { - RandID int64 - GuestUUID [16]byte - GuestID int64 - IsReq int32 - IsResp int32 - NeedResponse int32 - ReqLength int32 - RespLength int32 - CommandID int32 - SubCommand int32 - Reserve int32 -} - -var GuestCPUs = flag.Int("cpu", 1, "Num of Guest cpu") -var Cluster_UUID = "3bdea92b-68f4-4fe9-aa4b-d645c3c63864" -var HostDSMfixNumber = flag.Int("fixNumber", 0, "Fix Number of Host") -var VMMTimestamp = flag.Int("vmmts", 1679863686, "VMM Timestamp") -var VMMVersion = flag.String("vmmversion", "2.6.1-12139", "VMM version") -var HostSN = flag.String("hostsn", "0000000000000", "Host SN, 13 bytes") -var GuestSN = flag.String("guestsn", "0000000000000", "Guest SN, 13 bytes") -var HostDSMBuildNumber = flag.Int("buildnumber", 42962, "Build Number of Host") -var GuestCPU_ARCH = flag.String("cpu_arch", "QEMU, Virtual CPU, X86_64", "CPU arch") -var GuestUUID = flag.String("guestuuid", "ba13a19a-c0c1-4fef-9346-915ed3b98341", "Guest UUID") - -var ApiPort = flag.String("api", ":2210", "API port") -var ListenAddr = flag.String("addr", "0.0.0.0:12345", "Listen address") - -var LastConnection net.Conn - -func main() { - - flag.Parse() - - r := mux.NewRouter() - r.HandleFunc("/", home) - r.HandleFunc("/write", write) - go http.ListenAndServe(*ApiPort, r) - - listener, err := net.Listen("tcp", *ListenAddr) - - if err != nil { - log.Fatalln("Error listening", err.Error()) - return - } - - log.Println("Start listen on " + *ListenAddr) - - for { - conn, err := listener.Accept() - if err != nil { - log.Fatalln("Error on accept", err.Error()) - return - } - log.Printf("New connection from %s\n", conn.RemoteAddr().String()) - - go incoming_conn(conn) - } -} - -func incoming_conn(conn net.Conn) { - - LastConnection = conn - - for { - buf := make([]byte, 4096) - len, err := conn.Read(buf) - if err != nil { - log.Println("Error on read", err.Error()) - return - } - if len != 4096 { - log.Printf("Read %d Bytes, not 4096\n", len) - // something wrong, close and wait for reconnect - conn.Close() - return - } - go process_req(buf, conn) - //log.Printf("Read %d Bytes\n%#v\n", len, string(buf[:len])) - } -} - -var commandsName = map[int]string{ - 3: "Guest Power info", - 4: "Host DSM version", - 5: "Guest SN", - 7: "Guest CPU info", - 9: "Host DSM version", - 8: "VMM version", - 10: "Get Guest Info", - 11: "Guest UUID", - 12: "Cluster UUID", - 13: "Host SN", - 16: "Update Deadline", - 17: "Guest Timestamp", -} - -func process_req(buf []byte, conn net.Conn) { - var req REQ - var data string - err := binary.Read(bytes.NewReader(buf), binary.LittleEndian, &req) - if err != nil { - log.Printf("Error on decode %s\n", err) - return - } - - if req.IsReq == 1 { - data = string(buf[64 : 64+req.ReqLength]) - } else if req.IsResp == 1 { - data = string(buf[64 : 64+req.RespLength]) - } - - // log.Printf("%#v\n", req) - log.Printf("Command: %s from Guest:%d \n", commandsName[int(req.CommandID)], req.GuestID) - if data != "" { - log.Printf("Info: %s\n", data) - } - // Hard code of command - switch req.CommandID { - case 3: - // Guest start/reboot - case 4: - // Host DSM version - data = fmt.Sprintf(`{"buildnumber":%d,"smallfixnumber":%d}`, *HostDSMBuildNumber, *HostDSMfixNumber) - case 5: - // Guest SN - data = *GuestSN - case 7: - // CPU info - // {"cpuinfo":"QEMU, Virtual CPU, X86_64, 1" "vcpu_num":1} - data = fmt.Sprintf(`{"cpuinfo":"%s","vcpu_num":%d}`, - *GuestCPU_ARCH+", "+strconv.Itoa(*GuestCPUs), *GuestCPUs) - case 8: - data = fmt.Sprintf(`{"id":"Virtualization","name":"Virtual Machine Manager","timestamp":%d,"version":"%s"}`, - *VMMTimestamp, *VMMVersion) - case 9: - // Version Info - case 10: - // Guest Info - case 11: - // Guest UUID - data = *GuestUUID - case 12: - // cluster UUID - data = Cluster_UUID - case 13: - // Host SN - data = *HostSN - case 16: - // Update Dead line time, always 0x7fffffffffffffff - data = "9223372036854775807" - case 17: - // TimeStamp - default: - log.Printf("No handler for this command %d\n", req.CommandID) - return - } - - // if it's a req and need response - if req.IsReq == 1 && req.NeedResponse == 1 { - buf = make([]byte, 0, 4096) - writer := bytes.NewBuffer(buf) - req.IsResp = 1 - req.IsReq = 0 - req.ReqLength = 0 - req.RespLength = int32(len([]byte(data)) + 1) - log.Printf("Response data: %s\n", data) - - // write to buf - binary.Write(writer, binary.LittleEndian, &req) - writer.Write([]byte(data)) - res := writer.Bytes() - // full fill 4096 - buf = make([]byte, 4096, 4096) - copy(buf, res) - conn.Write(buf) - } -} - -func home(w http.ResponseWriter, r *http.Request) { - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(`{"status": "error", "data": null, "message": "No command specified"}`)) -} - - -func write(w http.ResponseWriter, r *http.Request) { - - w.Header().Set("Content-Type", "application/json") - - var err error - var commandID int - - query := r.URL.Query() - commandID, err = strconv.Atoi(query.Get("command")) - - if (err != nil || commandID < 1) { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(`{"status": "error", "data": null, "message": "Invalid command ID"}`)) - return - } - - if (send_command((int32)(commandID), 1) == false) { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(`{"status": "error", "data": null, "message": "Failed to send command"}`)) - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte(`{"status": "success", "data": null, "message": null}`)) - return -} - -func send_command(CommandID int32, SubCommand int32) bool { - - var req REQ - - req.CommandID = CommandID - req.SubCommand = SubCommand - - req.IsReq = 1 - req.IsResp = 0 - req.ReqLength = 0 - req.RespLength = 0 - req.NeedResponse = 0 - req.GuestID = 10000000 - req.RandID = rand.Int63() - - var buf = make([]byte, 0, 4096) - var writer = bytes.NewBuffer(buf) - - // write to buf - binary.Write(writer, binary.LittleEndian, &req) - res := writer.Bytes() - - // full fill 4096 - buf = make([]byte, 4096, 4096) - copy(buf, res) - - //log.Printf("Writing command %d\n", CommandID) - - if (LastConnection == nil) { return false } - - LastConnection.Write(buf) - return true - -}