몇일동안 인터넷 검색해서 나온 해결법 다 해보고도 해결 못한 문제가 “윈도우 시간 동기화 실패” 문제입니다. 이사하기 전 KT 쓸때는 아무 문제 없었는데 이사후 지역 케이블 인터넷으로 바뀐뒤 부터 안되는걸 보니 제가 뭘해도 안될거 같아서 포기하고 윈도우 시간 동기화 하는 프로그램을 하나 만들었습니다.
func IsAdmin() bool { _, err := os.Open("\\.\PHYSICALDRIVE0") if err != nil { //관리자 권한이 없음 return false } //관리자 권한이 있음. return true }
윈도우에서 시간값 변경을 할려면 관리자 권한이 필요합니다. 위 코드는 단순하게 권한이 있는지 없는지 유무만 체크합니다.
func runMeElevated() { verb := "runas" exe, _ := os.Executable() cwd, _ := os.Getwd() args := strings.Join(os.Args[1:], " ") verbPtr, _ := syscall.UTF16PtrFromString(verb) exePtr, _ := syscall.UTF16PtrFromString(exe) cwdPtr, _ := syscall.UTF16PtrFromString(cwd) argPtr, _ := syscall.UTF16PtrFromString(args) // var showCmd int32 = 0 //SW_HIDE var showCmd int32 = 5 //SW_SHOW err := windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd) if err != nil { panic(err) } }
이 코드는 현재 실행파일을 관리자 권한으로 다시 실행 하는 코드입니다.
if IsAdmin() { //관리자 권한으로 실행시킬 코드들 } else { //관리자가 아니면 관리자 권한으로 다시 실행함 runMeElevated() }
위 함수 두개를 조합해서 이렇게 사용합니다.
var ( Kernel32dll = windows.NewLazyDLL("Kernel32.dll") procSystemParamInfo = Kernel32dll.NewProc("SetLocalTime") )
시스템 시간을 변경할려면 WinAPI중 SetLocalTime 함수를 사용해야 합니다.
type SYSTEMTIME struct { Year uint16 Month uint16 DayOfWeek uint16 Day uint16 Hour uint16 Minute uint16 Second uint16 Milliseconds uint16 }
SetLocalTime에서 사용할 SYSTEMTIME 구조체를 선언해줍니다.
ntptime, err := ntp.Time("time.google.com")
NTP 라이브러리로 github.com/beevik/ntp 를 사용했습니다.
st := SYSTEMTIME{} st.Year = uint16(ntptime.Year()) st.Month = uint16(ntptime.Month()) st.Day = uint16(ntptime.Day()) st.Hour = uint16(ntptime.Hour()) st.Minute = uint16(ntptime.Minute()) st.Second = uint16(ntptime.Second()) st.Milliseconds = uint16(ntptime.Nanosecond() / 1000000) procSystemParamInfo.Call(uintptr(unsafe.Pointer(&st)))
구한 ntptime을 구조체에 채운뒤 SetLocalTime를 호출하면 됩니다.
이하 전체 코드입니다.
package main import ( "encoding/json" "fmt" "time" "unsafe" "os" "strings" "syscall" "github.com/beevik/ntp" "golang.org/x/sys/windows" ) // https://anubissec.github.io/How-To-Call-Windows-APIs-In-Golang/# var ( Kernel32dll = windows.NewLazyDLL("Kernel32.dll") //procSystemParamInfo = Kernel32dll.NewProc("SetSystemTime") procSystemParamInfo = Kernel32dll.NewProc("SetLocalTime") ) type SYSTEMTIME struct { Year uint16 Month uint16 DayOfWeek uint16 Day uint16 Hour uint16 Minute uint16 Second uint16 Milliseconds uint16 } // --------------------------------------------------------------------------------------------- // 관리자 권한 체크 func IsAdmin() bool { _, err := os.Open("\\.\PHYSICALDRIVE0") if err != nil { //관리자 권한이 없음 return false } //관리자 권한이 있음. return true } // --------------------------------------------------------------------------------------------- func runMeElevated() { verb := "runas" exe, _ := os.Executable() cwd, _ := os.Getwd() args := strings.Join(os.Args[1:], " ") verbPtr, _ := syscall.UTF16PtrFromString(verb) exePtr, _ := syscall.UTF16PtrFromString(exe) cwdPtr, _ := syscall.UTF16PtrFromString(cwd) argPtr, _ := syscall.UTF16PtrFromString(args) // var showCmd int32 = 0 //SW_HIDE var showCmd int32 = 5 //SW_SHOW err := windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd) if err != nil { panic(err) } } // --------------------------------------------------------------------------------------------- func checkErr(err error) { if err != nil { panic(err) } } type Config struct { //DB관련 설정값 NTP_URL_LIST string `json:"ntp_url_list"` } // --------------------------------------------------------------------------------------------- // LoadConfig 는 설정파일을 읽어들인다. func LoadConfig(filePath string, result any) bool { bytes, err := os.ReadFile(filePath) if err != nil { fmt.Println(err) fo, err := os.Create(filePath) checkErr(err) defer fo.Close() jsonBytes, err := json.MarshalIndent(result, "", " ") checkErr(err) _, err = fo.Write(jsonBytes) checkErr(err) fmt.Println(filePath, "파일을 생성했습니다.") fmt.Println("json 파일을 열어서 각 항목을 수정하세요") return false } err = json.Unmarshal(bytes, &result) checkErr(err) return true } // --------------------------------------------------------------------------------------------- func main() { //설정파일 값 읽기 cf := Config{} if !LoadConfig("./timesync.json", &cf) { //설정파일 읽기 실패시 종료. os.Exit(0) } //관리자 권한 체크 if IsAdmin() { //관리자 권한일 경우 URLS := strings.Split(cf.NTP_URL_LIST, ",") for _, url := range URLS { url = strings.Replace(url, " ", "", -1) ntptime, err := ntp.Time(url) if err != nil { fmt.Println(err) continue } st := SYSTEMTIME{} st.Year = uint16(ntptime.Year()) st.Month = uint16(ntptime.Month()) st.Day = uint16(ntptime.Day()) st.Hour = uint16(ntptime.Hour()) st.Minute = uint16(ntptime.Minute()) st.Second = uint16(ntptime.Second()) st.Milliseconds = uint16(ntptime.Nanosecond() / 1000000) procSystemParamInfo.Call(uintptr(unsafe.Pointer(&st))) t := time.Now() t2 := t.Sub(ntptime) fmt.Printf("변경전 %s 서버와 로컬 시간 차이 : %s n", url, t2) time.Sleep(time.Millisecond * 100) ntptime, err = ntp.Time(url) if err != nil { fmt.Println(err) continue } t = time.Now() t2 = t.Sub(ntptime) fmt.Printf("변경후 %s 서버와 로컬 시간 차이 : %s n", url, t2) break } //------------------------------------------------------------------ fmt.Println("엔터키 누르면 창이 닫힙니다.") fmt.Scanln() } else { //관리자가 아니면 관리자 권한으로 다시 실행함 runMeElevated() } }
답글 남기기