Compare commits
No commits in common. "master" and "v1.1.2" have entirely different histories.
3
.gitignore
vendored
3
.gitignore
vendored
@ -29,9 +29,6 @@
|
|||||||
# Test binary, built with `go test -c`
|
# Test binary, built with `go test -c`
|
||||||
*.test
|
*.test
|
||||||
|
|
||||||
# Test file
|
|
||||||
fsutils/testfile
|
|
||||||
|
|
||||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
*.out
|
*.out
|
||||||
|
|
||||||
|
@ -1,155 +1,155 @@
|
|||||||
package protos
|
package protos
|
||||||
|
|
||||||
var (
|
var (
|
||||||
RegisteredProtoHOPOPT0 *IPProto = &IPProto{Name:"HOPOPT", Description:"IPv6 Hop-by-Hop Option", Number:0x0, csvNum:"", Reference:"[RFC8200]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoHOPOPT0 := &protos.IPProto{Name:"HOPOPT", Description:"IPv6 Hop-by-Hop Option", Number:0x0, csvNum:"", Reference:"[RFC8200]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoICMP0 = &IPProto{Name:"ICMP", Description:"Internet Control Message", Number:0x0, csvNum:"", Reference:"[RFC792]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoICMP0 := &protos.IPProto{Name:"ICMP", Description:"Internet Control Message", Number:0x0, csvNum:"", Reference:"[RFC792]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIGMP0 = &IPProto{Name:"IGMP", Description:"Internet Group Management", Number:0x0, csvNum:"", Reference:"[RFC1112]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIGMP0 := &protos.IPProto{Name:"IGMP", Description:"Internet Group Management", Number:0x0, csvNum:"", Reference:"[RFC1112]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoGGP0 = &IPProto{Name:"GGP", Description:"Gateway-to-Gateway", Number:0x0, csvNum:"", Reference:"[RFC823]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoGGP0 := &protos.IPProto{Name:"GGP", Description:"Gateway-to-Gateway", Number:0x0, csvNum:"", Reference:"[RFC823]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIPv40 = &IPProto{Name:"IPv4", Description:"IPv4 encapsulation", Number:0x0, csvNum:"", Reference:"[RFC2003]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIPv40 := &protos.IPProto{Name:"IPv4", Description:"IPv4 encapsulation", Number:0x0, csvNum:"", Reference:"[RFC2003]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoST0 = &IPProto{Name:"ST", Description:"Stream", Number:0x0, csvNum:"", Reference:"[RFC1190][RFC1819]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoST0 := &protos.IPProto{Name:"ST", Description:"Stream", Number:0x0, csvNum:"", Reference:"[RFC1190][RFC1819]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoTCP0 = &IPProto{Name:"TCP", Description:"Transmission Control", Number:0x0, csvNum:"", Reference:"[RFC793]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoTCP0 := &protos.IPProto{Name:"TCP", Description:"Transmission Control", Number:0x0, csvNum:"", Reference:"[RFC793]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoCBT0 = &IPProto{Name:"CBT", Description:"CBT", Number:0x0, csvNum:"", Reference:"[Tony_Ballardie]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoCBT0 := &protos.IPProto{Name:"CBT", Description:"CBT", Number:0x0, csvNum:"", Reference:"[Tony_Ballardie]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoEGP0 = &IPProto{Name:"EGP", Description:"Exterior Gateway Protocol", Number:0x0, csvNum:"", Reference:"[RFC888][David_Mills]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoEGP0 := &protos.IPProto{Name:"EGP", Description:"Exterior Gateway Protocol", Number:0x0, csvNum:"", Reference:"[RFC888][David_Mills]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIGP0 = &IPProto{Name:"IGP", Description:"any private interior gateway \n(used by Cisco for their IGRP)", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIGP0 := &protos.IPProto{Name:"IGP", Description:"any private interior gateway \n(used by Cisco for their IGRP)", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoBBN_RCC_MON0 = &IPProto{Name:"BBN-RCC-MON", Description:"BBN RCC Monitoring", Number:0x0, csvNum:"", Reference:"[Steve_Chipman]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoBBN-RCC-MON0 := &protos.IPProto{Name:"BBN-RCC-MON", Description:"BBN RCC Monitoring", Number:0x0, csvNum:"", Reference:"[Steve_Chipman]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoNVP_II0 = &IPProto{Name:"NVP-II", Description:"Network Voice Protocol", Number:0x0, csvNum:"", Reference:"[RFC741][Steve_Casner]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoNVP-II0 := &protos.IPProto{Name:"NVP-II", Description:"Network Voice Protocol", Number:0x0, csvNum:"", Reference:"[RFC741][Steve_Casner]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoPUP0 = &IPProto{Name:"PUP", Description:"PUP", Number:0x0, csvNum:"", Reference:"[Boggs, D., J. Shoch, E. Taft, and R. Metcalfe, \"PUP: An\nInternetwork Architecture\", XEROX Palo Alto Research Center,\nCSL-79-10, July 1979; also in IEEE Transactions on\nCommunication, Volume COM-28, Number 4, April 1980.][[XEROX]]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoPUP0 := &protos.IPProto{Name:"PUP", Description:"PUP", Number:0x0, csvNum:"", Reference:"[Boggs, D., J. Shoch, E. Taft, and R. Metcalfe, \"PUP: An\nInternetwork Architecture\", XEROX Palo Alto Research Center,\nCSL-79-10, July 1979; also in IEEE Transactions on\nCommunication, Volume COM-28, Number 4, April 1980.][[XEROX]]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoARGUS = &IPProto{Name:"ARGUS (deprecated)", Description:"ARGUS", Number:0x0, csvNum:"", Reference:"[Robert_W_Scheifler]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoARGUS (deprecated)0 := &protos.IPProto{Name:"ARGUS (deprecated)", Description:"ARGUS", Number:0x0, csvNum:"", Reference:"[Robert_W_Scheifler]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoEMCON0 = &IPProto{Name:"EMCON", Description:"EMCON", Number:0x0, csvNum:"", Reference:"[<mystery contact>]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoEMCON0 := &protos.IPProto{Name:"EMCON", Description:"EMCON", Number:0x0, csvNum:"", Reference:"[<mystery contact>]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoXNET0 = &IPProto{Name:"XNET", Description:"Cross Net Debugger", Number:0x0, csvNum:"", Reference:"[Haverty, J., \"XNET Formats for Internet Protocol Version 4\",\nIEN 158, October 1980.][Jack_Haverty]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoXNET0 := &protos.IPProto{Name:"XNET", Description:"Cross Net Debugger", Number:0x0, csvNum:"", Reference:"[Haverty, J., \"XNET Formats for Internet Protocol Version 4\",\nIEN 158, October 1980.][Jack_Haverty]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoCHAOS0 = &IPProto{Name:"CHAOS", Description:"Chaos", Number:0x0, csvNum:"", Reference:"[J_Noel_Chiappa]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoCHAOS0 := &protos.IPProto{Name:"CHAOS", Description:"Chaos", Number:0x0, csvNum:"", Reference:"[J_Noel_Chiappa]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoUDP0 = &IPProto{Name:"UDP", Description:"User Datagram", Number:0x0, csvNum:"", Reference:"[RFC768][Jon_Postel]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoUDP0 := &protos.IPProto{Name:"UDP", Description:"User Datagram", Number:0x0, csvNum:"", Reference:"[RFC768][Jon_Postel]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoMUX0 = &IPProto{Name:"MUX", Description:"Multiplexing", Number:0x0, csvNum:"", Reference:"[Cohen, D. and J. Postel, \"Multiplexing Protocol\", IEN 90,\nUSC/Information Sciences Institute, May 1979.][Jon_Postel]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoMUX0 := &protos.IPProto{Name:"MUX", Description:"Multiplexing", Number:0x0, csvNum:"", Reference:"[Cohen, D. and J. Postel, \"Multiplexing Protocol\", IEN 90,\nUSC/Information Sciences Institute, May 1979.][Jon_Postel]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoDCN_MEAS0 = &IPProto{Name:"DCN-MEAS", Description:"DCN Measurement Subsystems", Number:0x0, csvNum:"", Reference:"[David_Mills]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoDCN-MEAS0 := &protos.IPProto{Name:"DCN-MEAS", Description:"DCN Measurement Subsystems", Number:0x0, csvNum:"", Reference:"[David_Mills]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoHMP0 = &IPProto{Name:"HMP", Description:"Host Monitoring", Number:0x0, csvNum:"", Reference:"[RFC869][Bob_Hinden]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoHMP0 := &protos.IPProto{Name:"HMP", Description:"Host Monitoring", Number:0x0, csvNum:"", Reference:"[RFC869][Bob_Hinden]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoPRM0 = &IPProto{Name:"PRM", Description:"Packet Radio Measurement", Number:0x0, csvNum:"", Reference:"[Zaw_Sing_Su]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoPRM0 := &protos.IPProto{Name:"PRM", Description:"Packet Radio Measurement", Number:0x0, csvNum:"", Reference:"[Zaw_Sing_Su]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoXNS_IDP0 = &IPProto{Name:"XNS-IDP", Description:"XEROX NS IDP", Number:0x0, csvNum:"", Reference:"[\"The Ethernet, A Local Area Network: Data Link Layer and\nPhysical Layer Specification\", AA-K759B-TK, Digital\nEquipment Corporation, Maynard, MA. Also as: \"The\nEthernet - A Local Area Network\", Version 1.0, Digital\nEquipment Corporation, Intel Corporation, Xerox\nCorporation, September 1980. And: \"The Ethernet, A Local\nArea Network: Data Link Layer and Physical Layer\nSpecifications\", Digital, Intel and Xerox, November 1982.\nAnd: XEROX, \"The Ethernet, A Local Area Network: Data Link\nLayer and Physical Layer Specification\", X3T51/80-50,\nXerox Corporation, Stamford, CT., October 1980.][[XEROX]]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoXNS-IDP0 := &protos.IPProto{Name:"XNS-IDP", Description:"XEROX NS IDP", Number:0x0, csvNum:"", Reference:"[\"The Ethernet, A Local Area Network: Data Link Layer and\nPhysical Layer Specification\", AA-K759B-TK, Digital\nEquipment Corporation, Maynard, MA. Also as: \"The\nEthernet - A Local Area Network\", Version 1.0, Digital\nEquipment Corporation, Intel Corporation, Xerox\nCorporation, September 1980. And: \"The Ethernet, A Local\nArea Network: Data Link Layer and Physical Layer\nSpecifications\", Digital, Intel and Xerox, November 1982.\nAnd: XEROX, \"The Ethernet, A Local Area Network: Data Link\nLayer and Physical Layer Specification\", X3T51/80-50,\nXerox Corporation, Stamford, CT., October 1980.][[XEROX]]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoTRUNK_10 = &IPProto{Name:"TRUNK-1", Description:"Trunk-1", Number:0x0, csvNum:"", Reference:"[Barry_Boehm]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoTRUNK-10 := &protos.IPProto{Name:"TRUNK-1", Description:"Trunk-1", Number:0x0, csvNum:"", Reference:"[Barry_Boehm]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoTRUNK_20 = &IPProto{Name:"TRUNK-2", Description:"Trunk-2", Number:0x0, csvNum:"", Reference:"[Barry_Boehm]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoTRUNK-20 := &protos.IPProto{Name:"TRUNK-2", Description:"Trunk-2", Number:0x0, csvNum:"", Reference:"[Barry_Boehm]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoLEAF_10 = &IPProto{Name:"LEAF-1", Description:"Leaf-1", Number:0x0, csvNum:"", Reference:"[Barry_Boehm]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoLEAF-10 := &protos.IPProto{Name:"LEAF-1", Description:"Leaf-1", Number:0x0, csvNum:"", Reference:"[Barry_Boehm]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoLEAF_20 = &IPProto{Name:"LEAF-2", Description:"Leaf-2", Number:0x0, csvNum:"", Reference:"[Barry_Boehm]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoLEAF-20 := &protos.IPProto{Name:"LEAF-2", Description:"Leaf-2", Number:0x0, csvNum:"", Reference:"[Barry_Boehm]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoRDP0 = &IPProto{Name:"RDP", Description:"Reliable Data Protocol", Number:0x0, csvNum:"", Reference:"[RFC908][Bob_Hinden]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoRDP0 := &protos.IPProto{Name:"RDP", Description:"Reliable Data Protocol", Number:0x0, csvNum:"", Reference:"[RFC908][Bob_Hinden]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIRTP0 = &IPProto{Name:"IRTP", Description:"Internet Reliable Transaction", Number:0x0, csvNum:"", Reference:"[RFC938][Trudy_Miller]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIRTP0 := &protos.IPProto{Name:"IRTP", Description:"Internet Reliable Transaction", Number:0x0, csvNum:"", Reference:"[RFC938][Trudy_Miller]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoISO-TP40 = &IPProto{Name:"ISO-TP4", Description:"ISO Transport Protocol Class 4", Number:0x0, csvNum:"", Reference:"[RFC905][<mystery contact>]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoISO-TP40 := &protos.IPProto{Name:"ISO-TP4", Description:"ISO Transport Protocol Class 4", Number:0x0, csvNum:"", Reference:"[RFC905][<mystery contact>]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoNETBLT0 = &IPProto{Name:"NETBLT", Description:"Bulk Data Transfer Protocol", Number:0x0, csvNum:"", Reference:"[RFC969][David_Clark]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoNETBLT0 := &protos.IPProto{Name:"NETBLT", Description:"Bulk Data Transfer Protocol", Number:0x0, csvNum:"", Reference:"[RFC969][David_Clark]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoMFE-NSP0 = &IPProto{Name:"MFE-NSP", Description:"MFE Network Services Protocol", Number:0x0, csvNum:"", Reference:"[Shuttleworth, B., \"A Documentary of MFENet, a National\nComputer Network\", UCRL-52317, Lawrence Livermore Labs,\nLivermore, California, June 1977.][Barry_Howard]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoMFE-NSP0 := &protos.IPProto{Name:"MFE-NSP", Description:"MFE Network Services Protocol", Number:0x0, csvNum:"", Reference:"[Shuttleworth, B., \"A Documentary of MFENet, a National\nComputer Network\", UCRL-52317, Lawrence Livermore Labs,\nLivermore, California, June 1977.][Barry_Howard]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoMERIT-INP0 = &IPProto{Name:"MERIT-INP", Description:"MERIT Internodal Protocol", Number:0x0, csvNum:"", Reference:"[Hans_Werner_Braun]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoMERIT-INP0 := &protos.IPProto{Name:"MERIT-INP", Description:"MERIT Internodal Protocol", Number:0x0, csvNum:"", Reference:"[Hans_Werner_Braun]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoDCCP0 = &IPProto{Name:"DCCP", Description:"Datagram Congestion Control Protocol", Number:0x0, csvNum:"", Reference:"[RFC4340]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoDCCP0 := &protos.IPProto{Name:"DCCP", Description:"Datagram Congestion Control Protocol", Number:0x0, csvNum:"", Reference:"[RFC4340]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProto3PC0 = &IPProto{Name:"3PC", Description:"Third Party Connect Protocol", Number:0x0, csvNum:"", Reference:"[Stuart_A_Friedberg]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProto3PC0 := &protos.IPProto{Name:"3PC", Description:"Third Party Connect Protocol", Number:0x0, csvNum:"", Reference:"[Stuart_A_Friedberg]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIDPR0 = &IPProto{Name:"IDPR", Description:"Inter-Domain Policy Routing Protocol", Number:0x0, csvNum:"", Reference:"[Martha_Steenstrup]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIDPR0 := &protos.IPProto{Name:"IDPR", Description:"Inter-Domain Policy Routing Protocol", Number:0x0, csvNum:"", Reference:"[Martha_Steenstrup]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoXTP0 = &IPProto{Name:"XTP", Description:"XTP", Number:0x0, csvNum:"", Reference:"[Greg_Chesson]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoXTP0 := &protos.IPProto{Name:"XTP", Description:"XTP", Number:0x0, csvNum:"", Reference:"[Greg_Chesson]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoDDP0 = &IPProto{Name:"DDP", Description:"Datagram Delivery Protocol", Number:0x0, csvNum:"", Reference:"[Wesley_Craig]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoDDP0 := &protos.IPProto{Name:"DDP", Description:"Datagram Delivery Protocol", Number:0x0, csvNum:"", Reference:"[Wesley_Craig]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIDPR-CMTP0 = &IPProto{Name:"IDPR-CMTP", Description:"IDPR Control Message Transport Proto", Number:0x0, csvNum:"", Reference:"[Martha_Steenstrup]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIDPR-CMTP0 := &protos.IPProto{Name:"IDPR-CMTP", Description:"IDPR Control Message Transport Proto", Number:0x0, csvNum:"", Reference:"[Martha_Steenstrup]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoTP++0 = &IPProto{Name:"TP++", Description:"TP++ Transport Protocol", Number:0x0, csvNum:"", Reference:"[Dirk_Fromhein]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoTP++0 := &protos.IPProto{Name:"TP++", Description:"TP++ Transport Protocol", Number:0x0, csvNum:"", Reference:"[Dirk_Fromhein]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIL0 = &IPProto{Name:"IL", Description:"IL Transport Protocol", Number:0x0, csvNum:"", Reference:"[Dave_Presotto]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIL0 := &protos.IPProto{Name:"IL", Description:"IL Transport Protocol", Number:0x0, csvNum:"", Reference:"[Dave_Presotto]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIPv60 = &IPProto{Name:"IPv6", Description:"IPv6 encapsulation", Number:0x0, csvNum:"", Reference:"[RFC2473]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIPv60 := &protos.IPProto{Name:"IPv6", Description:"IPv6 encapsulation", Number:0x0, csvNum:"", Reference:"[RFC2473]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoSDRP0 = &IPProto{Name:"SDRP", Description:"Source Demand Routing Protocol", Number:0x0, csvNum:"", Reference:"[Deborah_Estrin]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoSDRP0 := &protos.IPProto{Name:"SDRP", Description:"Source Demand Routing Protocol", Number:0x0, csvNum:"", Reference:"[Deborah_Estrin]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIPv6-Route0 = &IPProto{Name:"IPv6-Route", Description:"Routing Header for IPv6", Number:0x0, csvNum:"", Reference:"[Steve_Deering]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIPv6-Route0 := &protos.IPProto{Name:"IPv6-Route", Description:"Routing Header for IPv6", Number:0x0, csvNum:"", Reference:"[Steve_Deering]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIPv6-Frag0 = &IPProto{Name:"IPv6-Frag", Description:"Fragment Header for IPv6", Number:0x0, csvNum:"", Reference:"[Steve_Deering]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIPv6-Frag0 := &protos.IPProto{Name:"IPv6-Frag", Description:"Fragment Header for IPv6", Number:0x0, csvNum:"", Reference:"[Steve_Deering]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIDRP0 = &IPProto{Name:"IDRP", Description:"Inter-Domain Routing Protocol", Number:0x0, csvNum:"", Reference:"[Sue_Hares]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIDRP0 := &protos.IPProto{Name:"IDRP", Description:"Inter-Domain Routing Protocol", Number:0x0, csvNum:"", Reference:"[Sue_Hares]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoRSVP0 = &IPProto{Name:"RSVP", Description:"Reservation Protocol", Number:0x0, csvNum:"", Reference:"[RFC2205][RFC3209][Bob_Braden]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoRSVP0 := &protos.IPProto{Name:"RSVP", Description:"Reservation Protocol", Number:0x0, csvNum:"", Reference:"[RFC2205][RFC3209][Bob_Braden]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoGRE0 = &IPProto{Name:"GRE", Description:"Generic Routing Encapsulation", Number:0x0, csvNum:"", Reference:"[RFC2784][Tony_Li]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoGRE0 := &protos.IPProto{Name:"GRE", Description:"Generic Routing Encapsulation", Number:0x0, csvNum:"", Reference:"[RFC2784][Tony_Li]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoDSR0 = &IPProto{Name:"DSR", Description:"Dynamic Source Routing Protocol", Number:0x0, csvNum:"", Reference:"[RFC4728]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoDSR0 := &protos.IPProto{Name:"DSR", Description:"Dynamic Source Routing Protocol", Number:0x0, csvNum:"", Reference:"[RFC4728]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoBNA0 = &IPProto{Name:"BNA", Description:"BNA", Number:0x0, csvNum:"", Reference:"[Gary Salamon]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoBNA0 := &protos.IPProto{Name:"BNA", Description:"BNA", Number:0x0, csvNum:"", Reference:"[Gary Salamon]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoESP0 = &IPProto{Name:"ESP", Description:"Encap Security Payload", Number:0x0, csvNum:"", Reference:"[RFC4303]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoESP0 := &protos.IPProto{Name:"ESP", Description:"Encap Security Payload", Number:0x0, csvNum:"", Reference:"[RFC4303]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoAH0 = &IPProto{Name:"AH", Description:"Authentication Header", Number:0x0, csvNum:"", Reference:"[RFC4302]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoAH0 := &protos.IPProto{Name:"AH", Description:"Authentication Header", Number:0x0, csvNum:"", Reference:"[RFC4302]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoI-NLSP0 = &IPProto{Name:"I-NLSP", Description:"Integrated Net Layer Security TUBA", Number:0x0, csvNum:"", Reference:"[K_Robert_Glenn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoI-NLSP0 := &protos.IPProto{Name:"I-NLSP", Description:"Integrated Net Layer Security TUBA", Number:0x0, csvNum:"", Reference:"[K_Robert_Glenn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoSWIPE (deprecated)0 = &IPProto{Name:"SWIPE (deprecated)", Description:"IP with Encryption", Number:0x0, csvNum:"", Reference:"[John_Ioannidis]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoSWIPE (deprecated)0 := &protos.IPProto{Name:"SWIPE (deprecated)", Description:"IP with Encryption", Number:0x0, csvNum:"", Reference:"[John_Ioannidis]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoNARP0 = &IPProto{Name:"NARP", Description:"NBMA Address Resolution Protocol", Number:0x0, csvNum:"", Reference:"[RFC1735]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoNARP0 := &protos.IPProto{Name:"NARP", Description:"NBMA Address Resolution Protocol", Number:0x0, csvNum:"", Reference:"[RFC1735]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoMOBILE0 = &IPProto{Name:"MOBILE", Description:"IP Mobility", Number:0x0, csvNum:"", Reference:"[Charlie_Perkins]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoMOBILE0 := &protos.IPProto{Name:"MOBILE", Description:"IP Mobility", Number:0x0, csvNum:"", Reference:"[Charlie_Perkins]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoTLSP0 = &IPProto{Name:"TLSP", Description:"Transport Layer Security Protocol \nusing Kryptonet key management", Number:0x0, csvNum:"", Reference:"[Christer_Oberg]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoTLSP0 := &protos.IPProto{Name:"TLSP", Description:"Transport Layer Security Protocol \nusing Kryptonet key management", Number:0x0, csvNum:"", Reference:"[Christer_Oberg]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoSKIP0 = &IPProto{Name:"SKIP", Description:"SKIP", Number:0x0, csvNum:"", Reference:"[Tom_Markson]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoSKIP0 := &protos.IPProto{Name:"SKIP", Description:"SKIP", Number:0x0, csvNum:"", Reference:"[Tom_Markson]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIPv6-ICMP0 = &IPProto{Name:"IPv6-ICMP", Description:"ICMP for IPv6", Number:0x0, csvNum:"", Reference:"[RFC8200]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIPv6-ICMP0 := &protos.IPProto{Name:"IPv6-ICMP", Description:"ICMP for IPv6", Number:0x0, csvNum:"", Reference:"[RFC8200]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIPv6-NoNxt0 = &IPProto{Name:"IPv6-NoNxt", Description:"No Next Header for IPv6", Number:0x0, csvNum:"", Reference:"[RFC8200]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIPv6-NoNxt0 := &protos.IPProto{Name:"IPv6-NoNxt", Description:"No Next Header for IPv6", Number:0x0, csvNum:"", Reference:"[RFC8200]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIPv6-Opts0 = &IPProto{Name:"IPv6-Opts", Description:"Destination Options for IPv6", Number:0x0, csvNum:"", Reference:"[RFC8200]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIPv6-Opts0 := &protos.IPProto{Name:"IPv6-Opts", Description:"Destination Options for IPv6", Number:0x0, csvNum:"", Reference:"[RFC8200]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProto0 = &IPProto{Name:"", Description:"any host internal protocol", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProto0 := &protos.IPProto{Name:"", Description:"any host internal protocol", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoCFTP0 = &IPProto{Name:"CFTP", Description:"CFTP", Number:0x0, csvNum:"", Reference:"[Forsdick, H., \"CFTP\", Network Message, Bolt Beranek and\nNewman, January 1982.][Harry_Forsdick]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoCFTP0 := &protos.IPProto{Name:"CFTP", Description:"CFTP", Number:0x0, csvNum:"", Reference:"[Forsdick, H., \"CFTP\", Network Message, Bolt Beranek and\nNewman, January 1982.][Harry_Forsdick]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProto0 = &IPProto{Name:"", Description:"any local network", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProto0 := &protos.IPProto{Name:"", Description:"any local network", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoSAT-EXPAK0 = &IPProto{Name:"SAT-EXPAK", Description:"SATNET and Backroom EXPAK", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoSAT-EXPAK0 := &protos.IPProto{Name:"SAT-EXPAK", Description:"SATNET and Backroom EXPAK", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoKRYPTOLAN0 = &IPProto{Name:"KRYPTOLAN", Description:"Kryptolan", Number:0x0, csvNum:"", Reference:"[Paul Liu]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoKRYPTOLAN0 := &protos.IPProto{Name:"KRYPTOLAN", Description:"Kryptolan", Number:0x0, csvNum:"", Reference:"[Paul Liu]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoRVD0 = &IPProto{Name:"RVD", Description:"MIT Remote Virtual Disk Protocol", Number:0x0, csvNum:"", Reference:"[Michael_Greenwald]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoRVD0 := &protos.IPProto{Name:"RVD", Description:"MIT Remote Virtual Disk Protocol", Number:0x0, csvNum:"", Reference:"[Michael_Greenwald]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIPPC0 = &IPProto{Name:"IPPC", Description:"Internet Pluribus Packet Core", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIPPC0 := &protos.IPProto{Name:"IPPC", Description:"Internet Pluribus Packet Core", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProto0 = &IPProto{Name:"", Description:"any distributed file system", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProto0 := &protos.IPProto{Name:"", Description:"any distributed file system", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoSAT-MON0 = &IPProto{Name:"SAT-MON", Description:"SATNET Monitoring", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoSAT-MON0 := &protos.IPProto{Name:"SAT-MON", Description:"SATNET Monitoring", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoVISA0 = &IPProto{Name:"VISA", Description:"VISA Protocol", Number:0x0, csvNum:"", Reference:"[Gene_Tsudik]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoVISA0 := &protos.IPProto{Name:"VISA", Description:"VISA Protocol", Number:0x0, csvNum:"", Reference:"[Gene_Tsudik]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIPCV0 = &IPProto{Name:"IPCV", Description:"Internet Packet Core Utility", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIPCV0 := &protos.IPProto{Name:"IPCV", Description:"Internet Packet Core Utility", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoCPNX0 = &IPProto{Name:"CPNX", Description:"Computer Protocol Network Executive", Number:0x0, csvNum:"", Reference:"[David Mittnacht]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoCPNX0 := &protos.IPProto{Name:"CPNX", Description:"Computer Protocol Network Executive", Number:0x0, csvNum:"", Reference:"[David Mittnacht]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoCPHB0 = &IPProto{Name:"CPHB", Description:"Computer Protocol Heart Beat", Number:0x0, csvNum:"", Reference:"[David Mittnacht]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoCPHB0 := &protos.IPProto{Name:"CPHB", Description:"Computer Protocol Heart Beat", Number:0x0, csvNum:"", Reference:"[David Mittnacht]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoWSN0 = &IPProto{Name:"WSN", Description:"Wang Span Network", Number:0x0, csvNum:"", Reference:"[Victor Dafoulas]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoWSN0 := &protos.IPProto{Name:"WSN", Description:"Wang Span Network", Number:0x0, csvNum:"", Reference:"[Victor Dafoulas]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoPVP0 = &IPProto{Name:"PVP", Description:"Packet Video Protocol", Number:0x0, csvNum:"", Reference:"[Steve_Casner]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoPVP0 := &protos.IPProto{Name:"PVP", Description:"Packet Video Protocol", Number:0x0, csvNum:"", Reference:"[Steve_Casner]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoBR-SAT-MON0 = &IPProto{Name:"BR-SAT-MON", Description:"Backroom SATNET Monitoring", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoBR-SAT-MON0 := &protos.IPProto{Name:"BR-SAT-MON", Description:"Backroom SATNET Monitoring", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoSUN-ND0 = &IPProto{Name:"SUN-ND", Description:"SUN ND PROTOCOL-Temporary", Number:0x0, csvNum:"", Reference:"[William_Melohn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoSUN-ND0 := &protos.IPProto{Name:"SUN-ND", Description:"SUN ND PROTOCOL-Temporary", Number:0x0, csvNum:"", Reference:"[William_Melohn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoWB-MON0 = &IPProto{Name:"WB-MON", Description:"WIDEBAND Monitoring", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoWB-MON0 := &protos.IPProto{Name:"WB-MON", Description:"WIDEBAND Monitoring", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoWB-EXPAK0 = &IPProto{Name:"WB-EXPAK", Description:"WIDEBAND EXPAK", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoWB-EXPAK0 := &protos.IPProto{Name:"WB-EXPAK", Description:"WIDEBAND EXPAK", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoISO-IP0 = &IPProto{Name:"ISO-IP", Description:"ISO Internet Protocol", Number:0x0, csvNum:"", Reference:"[Marshall_T_Rose]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoISO-IP0 := &protos.IPProto{Name:"ISO-IP", Description:"ISO Internet Protocol", Number:0x0, csvNum:"", Reference:"[Marshall_T_Rose]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoVMTP0 = &IPProto{Name:"VMTP", Description:"VMTP", Number:0x0, csvNum:"", Reference:"[Dave_Cheriton]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoVMTP0 := &protos.IPProto{Name:"VMTP", Description:"VMTP", Number:0x0, csvNum:"", Reference:"[Dave_Cheriton]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoSECURE-VMTP0 = &IPProto{Name:"SECURE-VMTP", Description:"SECURE-VMTP", Number:0x0, csvNum:"", Reference:"[Dave_Cheriton]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoSECURE-VMTP0 := &protos.IPProto{Name:"SECURE-VMTP", Description:"SECURE-VMTP", Number:0x0, csvNum:"", Reference:"[Dave_Cheriton]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoVINES0 = &IPProto{Name:"VINES", Description:"VINES", Number:0x0, csvNum:"", Reference:"[Brian Horn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoVINES0 := &protos.IPProto{Name:"VINES", Description:"VINES", Number:0x0, csvNum:"", Reference:"[Brian Horn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoTTP0 = &IPProto{Name:"TTP", Description:"Transaction Transport Protocol", Number:0x0, csvNum:"", Reference:"[Jim_Stevens]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoTTP0 := &protos.IPProto{Name:"TTP", Description:"Transaction Transport Protocol", Number:0x0, csvNum:"", Reference:"[Jim_Stevens]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIPTM0 = &IPProto{Name:"IPTM", Description:"Internet Protocol Traffic Manager", Number:0x0, csvNum:"", Reference:"[Jim_Stevens]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIPTM0 := &protos.IPProto{Name:"IPTM", Description:"Internet Protocol Traffic Manager", Number:0x0, csvNum:"", Reference:"[Jim_Stevens]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoNSFNET-IGP0 = &IPProto{Name:"NSFNET-IGP", Description:"NSFNET-IGP", Number:0x0, csvNum:"", Reference:"[Hans_Werner_Braun]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoNSFNET-IGP0 := &protos.IPProto{Name:"NSFNET-IGP", Description:"NSFNET-IGP", Number:0x0, csvNum:"", Reference:"[Hans_Werner_Braun]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoDGP0 = &IPProto{Name:"DGP", Description:"Dissimilar Gateway Protocol", Number:0x0, csvNum:"", Reference:"[M/A-COM Government Systems, \"Dissimilar Gateway Protocol\nSpecification, Draft Version\", Contract no. CS901145,\nNovember 16, 1987.][Mike_Little]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoDGP0 := &protos.IPProto{Name:"DGP", Description:"Dissimilar Gateway Protocol", Number:0x0, csvNum:"", Reference:"[M/A-COM Government Systems, \"Dissimilar Gateway Protocol\nSpecification, Draft Version\", Contract no. CS901145,\nNovember 16, 1987.][Mike_Little]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoTCF0 = &IPProto{Name:"TCF", Description:"TCF", Number:0x0, csvNum:"", Reference:"[Guillermo_A_Loyola]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoTCF0 := &protos.IPProto{Name:"TCF", Description:"TCF", Number:0x0, csvNum:"", Reference:"[Guillermo_A_Loyola]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoEIGRP0 = &IPProto{Name:"EIGRP", Description:"EIGRP", Number:0x0, csvNum:"", Reference:"[RFC7868]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoEIGRP0 := &protos.IPProto{Name:"EIGRP", Description:"EIGRP", Number:0x0, csvNum:"", Reference:"[RFC7868]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoOSPFIGP0 = &IPProto{Name:"OSPFIGP", Description:"OSPFIGP", Number:0x0, csvNum:"", Reference:"[RFC1583][RFC2328][RFC5340][John_Moy]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoOSPFIGP0 := &protos.IPProto{Name:"OSPFIGP", Description:"OSPFIGP", Number:0x0, csvNum:"", Reference:"[RFC1583][RFC2328][RFC5340][John_Moy]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoSprite-RPC0 = &IPProto{Name:"Sprite-RPC", Description:"Sprite RPC Protocol", Number:0x0, csvNum:"", Reference:"[Welch, B., \"The Sprite Remote Procedure Call System\",\nTechnical Report, UCB/Computer Science Dept., 86/302,\nUniversity of California at Berkeley, June 1986.][Bruce Willins]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoSprite-RPC0 := &protos.IPProto{Name:"Sprite-RPC", Description:"Sprite RPC Protocol", Number:0x0, csvNum:"", Reference:"[Welch, B., \"The Sprite Remote Procedure Call System\",\nTechnical Report, UCB/Computer Science Dept., 86/302,\nUniversity of California at Berkeley, June 1986.][Bruce Willins]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoLARP0 = &IPProto{Name:"LARP", Description:"Locus Address Resolution Protocol", Number:0x0, csvNum:"", Reference:"[Brian Horn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoLARP0 := &protos.IPProto{Name:"LARP", Description:"Locus Address Resolution Protocol", Number:0x0, csvNum:"", Reference:"[Brian Horn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoMTP0 = &IPProto{Name:"MTP", Description:"Multicast Transport Protocol", Number:0x0, csvNum:"", Reference:"[Susie_Armstrong]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoMTP0 := &protos.IPProto{Name:"MTP", Description:"Multicast Transport Protocol", Number:0x0, csvNum:"", Reference:"[Susie_Armstrong]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoAX.250 = &IPProto{Name:"AX.25", Description:"AX.25 Frames", Number:0x0, csvNum:"", Reference:"[Brian_Kantor]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoAX.250 := &protos.IPProto{Name:"AX.25", Description:"AX.25 Frames", Number:0x0, csvNum:"", Reference:"[Brian_Kantor]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIPIP0 = &IPProto{Name:"IPIP", Description:"IP-within-IP Encapsulation Protocol", Number:0x0, csvNum:"", Reference:"[John_Ioannidis]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIPIP0 := &protos.IPProto{Name:"IPIP", Description:"IP-within-IP Encapsulation Protocol", Number:0x0, csvNum:"", Reference:"[John_Ioannidis]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoMICP (deprecated)0 = &IPProto{Name:"MICP (deprecated)", Description:"Mobile Internetworking Control Pro.", Number:0x0, csvNum:"", Reference:"[John_Ioannidis]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoMICP (deprecated)0 := &protos.IPProto{Name:"MICP (deprecated)", Description:"Mobile Internetworking Control Pro.", Number:0x0, csvNum:"", Reference:"[John_Ioannidis]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoSCC-SP0 = &IPProto{Name:"SCC-SP", Description:"Semaphore Communications Sec. Pro.", Number:0x0, csvNum:"", Reference:"[Howard_Hart]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoSCC-SP0 := &protos.IPProto{Name:"SCC-SP", Description:"Semaphore Communications Sec. Pro.", Number:0x0, csvNum:"", Reference:"[Howard_Hart]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoETHERIP0 = &IPProto{Name:"ETHERIP", Description:"Ethernet-within-IP Encapsulation", Number:0x0, csvNum:"", Reference:"[RFC3378]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoETHERIP0 := &protos.IPProto{Name:"ETHERIP", Description:"Ethernet-within-IP Encapsulation", Number:0x0, csvNum:"", Reference:"[RFC3378]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoENCAP0 = &IPProto{Name:"ENCAP", Description:"Encapsulation Header", Number:0x0, csvNum:"", Reference:"[RFC1241][Robert_Woodburn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoENCAP0 := &protos.IPProto{Name:"ENCAP", Description:"Encapsulation Header", Number:0x0, csvNum:"", Reference:"[RFC1241][Robert_Woodburn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProto0 = &IPProto{Name:"", Description:"any private encryption scheme", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProto0 := &protos.IPProto{Name:"", Description:"any private encryption scheme", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoGMTP0 = &IPProto{Name:"GMTP", Description:"GMTP", Number:0x0, csvNum:"", Reference:"[[RXB5]]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoGMTP0 := &protos.IPProto{Name:"GMTP", Description:"GMTP", Number:0x0, csvNum:"", Reference:"[[RXB5]]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIFMP0 = &IPProto{Name:"IFMP", Description:"Ipsilon Flow Management Protocol", Number:0x0, csvNum:"", Reference:"[Bob_Hinden][November 1995, 1997.]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIFMP0 := &protos.IPProto{Name:"IFMP", Description:"Ipsilon Flow Management Protocol", Number:0x0, csvNum:"", Reference:"[Bob_Hinden][November 1995, 1997.]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoPNNI0 = &IPProto{Name:"PNNI", Description:"PNNI over IP", Number:0x0, csvNum:"", Reference:"[Ross_Callon]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoPNNI0 := &protos.IPProto{Name:"PNNI", Description:"PNNI over IP", Number:0x0, csvNum:"", Reference:"[Ross_Callon]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoPIM0 = &IPProto{Name:"PIM", Description:"Protocol Independent Multicast", Number:0x0, csvNum:"", Reference:"[RFC7761][Dino_Farinacci]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoPIM0 := &protos.IPProto{Name:"PIM", Description:"Protocol Independent Multicast", Number:0x0, csvNum:"", Reference:"[RFC7761][Dino_Farinacci]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoARIS0 = &IPProto{Name:"ARIS", Description:"ARIS", Number:0x0, csvNum:"", Reference:"[Nancy_Feldman]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoARIS0 := &protos.IPProto{Name:"ARIS", Description:"ARIS", Number:0x0, csvNum:"", Reference:"[Nancy_Feldman]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoSCPS0 = &IPProto{Name:"SCPS", Description:"SCPS", Number:0x0, csvNum:"", Reference:"[Robert_Durst]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoSCPS0 := &protos.IPProto{Name:"SCPS", Description:"SCPS", Number:0x0, csvNum:"", Reference:"[Robert_Durst]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoQNX0 = &IPProto{Name:"QNX", Description:"QNX", Number:0x0, csvNum:"", Reference:"[Michael_Hunter]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoQNX0 := &protos.IPProto{Name:"QNX", Description:"QNX", Number:0x0, csvNum:"", Reference:"[Michael_Hunter]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoA/N0 = &IPProto{Name:"A/N", Description:"Active Networks", Number:0x0, csvNum:"", Reference:"[Bob_Braden]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoA/N0 := &protos.IPProto{Name:"A/N", Description:"Active Networks", Number:0x0, csvNum:"", Reference:"[Bob_Braden]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIPComp0 = &IPProto{Name:"IPComp", Description:"IP Payload Compression Protocol", Number:0x0, csvNum:"", Reference:"[RFC2393]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIPComp0 := &protos.IPProto{Name:"IPComp", Description:"IP Payload Compression Protocol", Number:0x0, csvNum:"", Reference:"[RFC2393]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoSNP0 = &IPProto{Name:"SNP", Description:"Sitara Networks Protocol", Number:0x0, csvNum:"", Reference:"[Manickam_R_Sridhar]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoSNP0 := &protos.IPProto{Name:"SNP", Description:"Sitara Networks Protocol", Number:0x0, csvNum:"", Reference:"[Manickam_R_Sridhar]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoCompaq-Peer0 = &IPProto{Name:"Compaq-Peer", Description:"Compaq Peer Protocol", Number:0x0, csvNum:"", Reference:"[Victor_Volpe]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoCompaq-Peer0 := &protos.IPProto{Name:"Compaq-Peer", Description:"Compaq Peer Protocol", Number:0x0, csvNum:"", Reference:"[Victor_Volpe]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIPX-in-IP0 = &IPProto{Name:"IPX-in-IP", Description:"IPX in IP", Number:0x0, csvNum:"", Reference:"[CJ_Lee]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIPX-in-IP0 := &protos.IPProto{Name:"IPX-in-IP", Description:"IPX in IP", Number:0x0, csvNum:"", Reference:"[CJ_Lee]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoVRRP0 = &IPProto{Name:"VRRP", Description:"Virtual Router Redundancy Protocol", Number:0x0, csvNum:"", Reference:"[RFC5798]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoVRRP0 := &protos.IPProto{Name:"VRRP", Description:"Virtual Router Redundancy Protocol", Number:0x0, csvNum:"", Reference:"[RFC5798]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoPGM0 = &IPProto{Name:"PGM", Description:"PGM Reliable Transport Protocol", Number:0x0, csvNum:"", Reference:"[Tony_Speakman]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoPGM0 := &protos.IPProto{Name:"PGM", Description:"PGM Reliable Transport Protocol", Number:0x0, csvNum:"", Reference:"[Tony_Speakman]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProto0 = &IPProto{Name:"", Description:"any 0-hop protocol", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProto0 := &protos.IPProto{Name:"", Description:"any 0-hop protocol", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoL2TP0 = &IPProto{Name:"L2TP", Description:"Layer Two Tunneling Protocol", Number:0x0, csvNum:"", Reference:"[RFC3931][Bernard_Aboba]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoL2TP0 := &protos.IPProto{Name:"L2TP", Description:"Layer Two Tunneling Protocol", Number:0x0, csvNum:"", Reference:"[RFC3931][Bernard_Aboba]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoDDX0 = &IPProto{Name:"DDX", Description:"D-II Data Exchange (DDX)", Number:0x0, csvNum:"", Reference:"[John_Worley]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoDDX0 := &protos.IPProto{Name:"DDX", Description:"D-II Data Exchange (DDX)", Number:0x0, csvNum:"", Reference:"[John_Worley]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIATP0 = &IPProto{Name:"IATP", Description:"Interactive Agent Transfer Protocol", Number:0x0, csvNum:"", Reference:"[John_Murphy]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIATP0 := &protos.IPProto{Name:"IATP", Description:"Interactive Agent Transfer Protocol", Number:0x0, csvNum:"", Reference:"[John_Murphy]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoSTP0 = &IPProto{Name:"STP", Description:"Schedule Transfer Protocol", Number:0x0, csvNum:"", Reference:"[Jean_Michel_Pittet]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoSTP0 := &protos.IPProto{Name:"STP", Description:"Schedule Transfer Protocol", Number:0x0, csvNum:"", Reference:"[Jean_Michel_Pittet]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoSRP0 = &IPProto{Name:"SRP", Description:"SpectraLink Radio Protocol", Number:0x0, csvNum:"", Reference:"[Mark_Hamilton]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoSRP0 := &protos.IPProto{Name:"SRP", Description:"SpectraLink Radio Protocol", Number:0x0, csvNum:"", Reference:"[Mark_Hamilton]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoUTI0 = &IPProto{Name:"UTI", Description:"UTI", Number:0x0, csvNum:"", Reference:"[Peter_Lothberg]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoUTI0 := &protos.IPProto{Name:"UTI", Description:"UTI", Number:0x0, csvNum:"", Reference:"[Peter_Lothberg]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoSMP0 = &IPProto{Name:"SMP", Description:"Simple Message Protocol", Number:0x0, csvNum:"", Reference:"[Leif_Ekblad]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoSMP0 := &protos.IPProto{Name:"SMP", Description:"Simple Message Protocol", Number:0x0, csvNum:"", Reference:"[Leif_Ekblad]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoSM (deprecated)0 = &IPProto{Name:"SM (deprecated)", Description:"Simple Multicast Protocol", Number:0x0, csvNum:"", Reference:"[Jon_Crowcroft][draft-perlman-simple-multicast]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoSM (deprecated)0 := &protos.IPProto{Name:"SM (deprecated)", Description:"Simple Multicast Protocol", Number:0x0, csvNum:"", Reference:"[Jon_Crowcroft][draft-perlman-simple-multicast]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoPTP0 = &IPProto{Name:"PTP", Description:"Performance Transparency Protocol", Number:0x0, csvNum:"", Reference:"[Michael_Welzl]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoPTP0 := &protos.IPProto{Name:"PTP", Description:"Performance Transparency Protocol", Number:0x0, csvNum:"", Reference:"[Michael_Welzl]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoISIS over IPv40 = &IPProto{Name:"ISIS over IPv4", Description:"", Number:0x0, csvNum:"", Reference:"[Tony_Przygienda]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoISIS over IPv40 := &protos.IPProto{Name:"ISIS over IPv4", Description:"", Number:0x0, csvNum:"", Reference:"[Tony_Przygienda]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoFIRE0 = &IPProto{Name:"FIRE", Description:"", Number:0x0, csvNum:"", Reference:"[Criag_Partridge]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoFIRE0 := &protos.IPProto{Name:"FIRE", Description:"", Number:0x0, csvNum:"", Reference:"[Criag_Partridge]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoCRTP0 = &IPProto{Name:"CRTP", Description:"Combat Radio Transport Protocol", Number:0x0, csvNum:"", Reference:"[Robert_Sautter]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoCRTP0 := &protos.IPProto{Name:"CRTP", Description:"Combat Radio Transport Protocol", Number:0x0, csvNum:"", Reference:"[Robert_Sautter]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoCRUDP0 = &IPProto{Name:"CRUDP", Description:"Combat Radio User Datagram", Number:0x0, csvNum:"", Reference:"[Robert_Sautter]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoCRUDP0 := &protos.IPProto{Name:"CRUDP", Description:"Combat Radio User Datagram", Number:0x0, csvNum:"", Reference:"[Robert_Sautter]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoSSCOPMCE0 = &IPProto{Name:"SSCOPMCE", Description:"", Number:0x0, csvNum:"", Reference:"[Kurt_Waber]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoSSCOPMCE0 := &protos.IPProto{Name:"SSCOPMCE", Description:"", Number:0x0, csvNum:"", Reference:"[Kurt_Waber]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoIPLT0 = &IPProto{Name:"IPLT", Description:"", Number:0x0, csvNum:"", Reference:"[[Hollbach]]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoIPLT0 := &protos.IPProto{Name:"IPLT", Description:"", Number:0x0, csvNum:"", Reference:"[[Hollbach]]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoSPS0 = &IPProto{Name:"SPS", Description:"Secure Packet Shield", Number:0x0, csvNum:"", Reference:"[Bill_McIntosh]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoSPS0 := &protos.IPProto{Name:"SPS", Description:"Secure Packet Shield", Number:0x0, csvNum:"", Reference:"[Bill_McIntosh]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoPIPE0 = &IPProto{Name:"PIPE", Description:"Private IP Encapsulation within IP", Number:0x0, csvNum:"", Reference:"[Bernhard_Petri]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoPIPE0 := &protos.IPProto{Name:"PIPE", Description:"Private IP Encapsulation within IP", Number:0x0, csvNum:"", Reference:"[Bernhard_Petri]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoSCTP0 = &IPProto{Name:"SCTP", Description:"Stream Control Transmission Protocol", Number:0x0, csvNum:"", Reference:"[Randall_R_Stewart]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoSCTP0 := &protos.IPProto{Name:"SCTP", Description:"Stream Control Transmission Protocol", Number:0x0, csvNum:"", Reference:"[Randall_R_Stewart]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoFC0 = &IPProto{Name:"FC", Description:"Fibre Channel", Number:0x0, csvNum:"", Reference:"[Murali_Rajagopal][RFC6172]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoFC0 := &protos.IPProto{Name:"FC", Description:"Fibre Channel", Number:0x0, csvNum:"", Reference:"[Murali_Rajagopal][RFC6172]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoRSVP-E2E-IGNORE0 = &IPProto{Name:"RSVP-E2E-IGNORE", Description:"", Number:0x0, csvNum:"", Reference:"[RFC3175]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoRSVP-E2E-IGNORE0 := &protos.IPProto{Name:"RSVP-E2E-IGNORE", Description:"", Number:0x0, csvNum:"", Reference:"[RFC3175]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoMobility Header0 = &IPProto{Name:"Mobility Header", Description:"", Number:0x0, csvNum:"", Reference:"[RFC6275]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoMobility Header0 := &protos.IPProto{Name:"Mobility Header", Description:"", Number:0x0, csvNum:"", Reference:"[RFC6275]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoUDPLite0 = &IPProto{Name:"UDPLite", Description:"", Number:0x0, csvNum:"", Reference:"[RFC3828]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoUDPLite0 := &protos.IPProto{Name:"UDPLite", Description:"", Number:0x0, csvNum:"", Reference:"[RFC3828]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoMPLS-in-IP0 = &IPProto{Name:"MPLS-in-IP", Description:"", Number:0x0, csvNum:"", Reference:"[RFC4023]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoMPLS-in-IP0 := &protos.IPProto{Name:"MPLS-in-IP", Description:"", Number:0x0, csvNum:"", Reference:"[RFC4023]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtomanet0 = &IPProto{Name:"manet", Description:"MANET Protocols", Number:0x0, csvNum:"", Reference:"[RFC5498]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtomanet0 := &protos.IPProto{Name:"manet", Description:"MANET Protocols", Number:0x0, csvNum:"", Reference:"[RFC5498]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoHIP0 = &IPProto{Name:"HIP", Description:"Host Identity Protocol", Number:0x0, csvNum:"", Reference:"[RFC7401]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoHIP0 := &protos.IPProto{Name:"HIP", Description:"Host Identity Protocol", Number:0x0, csvNum:"", Reference:"[RFC7401]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoShim60 = &IPProto{Name:"Shim6", Description:"Shim6 Protocol", Number:0x0, csvNum:"", Reference:"[RFC5533]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoShim60 := &protos.IPProto{Name:"Shim6", Description:"Shim6 Protocol", Number:0x0, csvNum:"", Reference:"[RFC5533]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoWESP0 = &IPProto{Name:"WESP", Description:"Wrapped Encapsulating Security Payload", Number:0x0, csvNum:"", Reference:"[RFC5840]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoWESP0 := &protos.IPProto{Name:"WESP", Description:"Wrapped Encapsulating Security Payload", Number:0x0, csvNum:"", Reference:"[RFC5840]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoROHC0 = &IPProto{Name:"ROHC", Description:"Robust Header Compression", Number:0x0, csvNum:"", Reference:"[RFC5858]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoROHC0 := &protos.IPProto{Name:"ROHC", Description:"Robust Header Compression", Number:0x0, csvNum:"", Reference:"[RFC5858]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoEthernet0 = &IPProto{Name:"Ethernet", Description:"Ethernet", Number:0x0, csvNum:"", Reference:"[RFC-ietf-spring-srv6-network-programming-28]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoEthernet0 := &protos.IPProto{Name:"Ethernet", Description:"Ethernet", Number:0x0, csvNum:"", Reference:"[RFC-ietf-spring-srv6-network-programming-28]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProto0 = &IPProto{Name:"", Description:"Unassigned", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProto0 := &protos.IPProto{Name:"", Description:"Unassigned", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProto0 = &IPProto{Name:"", Description:"Use for experimentation and testing", Number:0x0, csvNum:"", Reference:"[RFC3692]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProto0 := &protos.IPProto{Name:"", Description:"Use for experimentation and testing", Number:0x0, csvNum:"", Reference:"[RFC3692]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProto0 = &IPProto{Name:"", Description:"Use for experimentation and testing", Number:0x0, csvNum:"", Reference:"[RFC3692]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProto0 := &protos.IPProto{Name:"", Description:"Use for experimentation and testing", Number:0x0, csvNum:"", Reference:"[RFC3692]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
RegisteredProtoReserved0 = &IPProto{Name:"Reserved", Description:"", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
RegisteredProtoReserved0 := &protos.IPProto{Name:"Reserved", Description:"", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -7,6 +7,4 @@ type IPProto struct {
|
|||||||
Reference string `csv:"Reference"`
|
Reference string `csv:"Reference"`
|
||||||
Reserved bool `csv:"-"`
|
Reserved bool `csv:"-"`
|
||||||
IP6ExtensionHeader bool `csv:"-"`
|
IP6ExtensionHeader bool `csv:"-"`
|
||||||
csvNum string
|
|
||||||
ip6ext bool
|
|
||||||
}
|
}
|
||||||
|
19
TODO
19
TODO
@ -1,28 +1,13 @@
|
|||||||
- refactor the elevation detection stuff. I'm not terribly happy with it.
|
|
||||||
|
|
||||||
- password generator utility/library
|
- password generator utility/library
|
||||||
-- incorporate with r00t2.io/pwgen
|
|
||||||
-- incorporate with https://github.com/tredoe/osutil ?
|
-- incorporate with https://github.com/tredoe/osutil ?
|
||||||
-- cli flag to dump flat hashes too (https://github.com/hlandau/passlib and others soon in pwgen)
|
-- cli flag to dump flat hashes too
|
||||||
|
--- https://github.com/hlandau/passlib
|
||||||
- auger needs to be build-constrained to linux.
|
|
||||||
|
|
||||||
- unit tests
|
- unit tests
|
||||||
|
|
||||||
- constants/vars for errors
|
- constants/vars for errors
|
||||||
|
|
||||||
- func and struct to return segregated system-level env vars vs. user env vars (mostly usable on windows) (see envs/.TODO.go.UNFINISHED)
|
- func and struct to return segregated system-level env vars vs. user env vars (mostly usable on windows) (see envs/.TODO.go.UNFINISHED)
|
||||||
-- https://www3.ntu.edu.sg/home/ehchua/programming/howto/Environment_Variables.html
|
|
||||||
-- windows:
|
|
||||||
--- https://docs.microsoft.com/en-us/windows/deployment/usmt/usmt-recognized-environment-variables
|
|
||||||
--- https://pureinfotech.com/list-environment-variables-windows-10/
|
|
||||||
--- https://gist.github.com/RebeccaWhit3/5dad8627b8227142e1bea432db3f8824
|
|
||||||
--- https://ss64.com/nt/syntax-variables.html
|
|
||||||
-- linux/XDG:
|
|
||||||
--- https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html
|
|
||||||
--- https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
|
||||||
-- macOS:
|
|
||||||
--- https://ss64.com/osx/syntax-env_vars.html
|
|
||||||
|
|
||||||
- validator for windows usernames, domains, etc. (for *NIX, https://unix.stackexchange.com/a/435120/284004)
|
- validator for windows usernames, domains, etc. (for *NIX, https://unix.stackexchange.com/a/435120/284004)
|
||||||
-- https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/naming-conventions-for-computer-domain-site-ou
|
-- https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/naming-conventions-for-computer-domain-site-ou
|
||||||
|
@ -1 +0,0 @@
|
|||||||
This module is still under work.
|
|
@ -1,41 +0,0 @@
|
|||||||
package auger
|
|
||||||
|
|
||||||
const (
|
|
||||||
augLensTpl string = "/augeas/load/%v" // A fmt.Sprintf string (single placeholder only) for Lens module roots.
|
|
||||||
augFsTree string = "/files"
|
|
||||||
augFsTpl string = augFsTree + "%v//%v" // A fmt.Sprintf string (first placeholder fspath, second placeholder includeDirective) for files to search for the includeDirective.
|
|
||||||
augInclTfm string = "incl" // The transformer keyword for Augeas includes.
|
|
||||||
augAppendSuffix string = "[last()+1]"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
dstPtrTrue bool = true
|
|
||||||
dstPtrFalse bool = false
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// PtrTrue and PtrFalse are convenience references for constructing an AugFlags if needed. It is recommended you do not change these values if you do not like being confused.
|
|
||||||
PtrTrue *bool = &dstPtrTrue
|
|
||||||
PtrFalse *bool = &dstPtrFalse
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
IncludeOptNone is the default include recursion option for Aug.RecursiveInclude.
|
|
||||||
* No special behavior is defined
|
|
||||||
* All include directives are assumed to refer:
|
|
||||||
* Explicitly/exclusively to file paths
|
|
||||||
* That must exist
|
|
||||||
*/
|
|
||||||
const IncludeOptNone includeOpt = 0
|
|
||||||
const (
|
|
||||||
// IncludeOptNoExist specifies that inclusions are allowed to not exist, otherwise an error will be raised while attempting to parse them.
|
|
||||||
IncludeOptNoExist includeOpt = 1 << iota
|
|
||||||
// IncludeOptGlobbing indicates that the inclusion system supports globbing (as supported by (github.com/gobwas/glob).Match).
|
|
||||||
IncludeOptGlobbing
|
|
||||||
// IncludeOptRegex indicates that the inclusion system supports matching by regex (as supported by regexp).
|
|
||||||
IncludeOptRegex
|
|
||||||
// IncludeOptDirs indicates that the inclusion system supports matching by directory.
|
|
||||||
IncludeOptDirs
|
|
||||||
// IncludeOptDirsRecursive indicates that the inclusion system also recurses into subdirectories of matched directories. Only used if IncludeOptDirs is also set.
|
|
||||||
IncludeOptDirsRecursive
|
|
||||||
)
|
|
111
auger/funcs.go
111
auger/funcs.go
@ -1,111 +0,0 @@
|
|||||||
package auger
|
|
||||||
|
|
||||||
import (
|
|
||||||
`io/fs`
|
|
||||||
`os`
|
|
||||||
`strings`
|
|
||||||
|
|
||||||
`honnef.co/go/augeas`
|
|
||||||
`r00t2.io/goutils/bitmask`
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
NewAuger returns an auger.Aug.
|
|
||||||
|
|
||||||
See:
|
|
||||||
https://pkg.go.dev/honnef.co/go/augeas#readme-examples
|
|
||||||
https://pkg.go.dev/honnef.co/go/augeas#New
|
|
||||||
for the `root` and `loadPath` parameters
|
|
||||||
(and, by extension, the `flags` paraemter; note that the `flags`
|
|
||||||
is an auger.AugFlags, not an augeas.Flag!).
|
|
||||||
|
|
||||||
`flags` may be nil.
|
|
||||||
*/
|
|
||||||
func NewAuger(root, loadPath string, flags *AugFlags) (aug *Aug, err error) {
|
|
||||||
|
|
||||||
aug = new(Aug)
|
|
||||||
|
|
||||||
if aug.aug, err = augeas.New(root, loadPath, flags.Eval()); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAugerFromAugeas returns a wrapped auger.Aug from a (honnef.co/go/augeas).Augeas.
|
|
||||||
func NewAugerFromAugeas(orig augeas.Augeas) (aug *Aug) {
|
|
||||||
|
|
||||||
aug = new(Aug)
|
|
||||||
aug.aug = orig
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
AugpathToFspath returns the filesystem path (i.e. an existing file) from an Augeas path.
|
|
||||||
|
|
||||||
It is *required* and expected that the Augeas standard /files prefix be removed first;
|
|
||||||
if not, it is assumed to be part of the filesystem path.
|
|
||||||
|
|
||||||
If a valid path cannot be determined, fsPath will be empty.
|
|
||||||
|
|
||||||
To be clear, a file must exist for fsPath to not be empty;
|
|
||||||
the way AugpathToFsPath works is it recurses bottom-up a
|
|
||||||
given path and checks for the existence of a file,
|
|
||||||
continuing upwards if not found.
|
|
||||||
*/
|
|
||||||
func AugpathToFspath(augPath string) (fsPath string, err error) {
|
|
||||||
|
|
||||||
var path string
|
|
||||||
var num int
|
|
||||||
var augSplit []string = strings.Split(augPath, "/")
|
|
||||||
|
|
||||||
num = len(augSplit)
|
|
||||||
|
|
||||||
for i := num - 1; i >= 0; i-- {
|
|
||||||
path = strings.Join(augSplit[:i], "/")
|
|
||||||
if !fs.ValidPath(path) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, err = os.Stat(path); err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
err = nil
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fsPath = path
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// dedupePaths is used to reduce new to only unique entries that do not exist in existing.
|
|
||||||
func dedupePaths(new, existing []string) (missing []string) {
|
|
||||||
|
|
||||||
var ok bool
|
|
||||||
var m map[string]bool = make(map[string]bool)
|
|
||||||
|
|
||||||
for _, path := range existing {
|
|
||||||
m[path] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, path := range new {
|
|
||||||
if _, ok = m[path]; !ok {
|
|
||||||
missing = append(missing, path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// getInclPaths applies path options to inclusions.
|
|
||||||
func getInclPaths(pathSpec string, inclFlags *bitmask.MaskBit) (fpaths []string, err error) {
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,322 +0,0 @@
|
|||||||
package auger
|
|
||||||
|
|
||||||
import (
|
|
||||||
`bufio`
|
|
||||||
`errors`
|
|
||||||
`fmt`
|
|
||||||
`io`
|
|
||||||
`os`
|
|
||||||
`path/filepath`
|
|
||||||
`strings`
|
|
||||||
|
|
||||||
`github.com/davecgh/go-spew/spew`
|
|
||||||
`github.com/google/shlex`
|
|
||||||
`honnef.co/go/augeas`
|
|
||||||
`r00t2.io/goutils/bitmask`
|
|
||||||
`r00t2.io/sysutils/paths`
|
|
||||||
)
|
|
||||||
|
|
||||||
// Close cleanly closes the underlying Augeas connection.
|
|
||||||
func (a *Aug) Close() {
|
|
||||||
|
|
||||||
a.aug.Close()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interact provides an interactive shell-like interface for debugging purposes to explore the loaded Augeas tree.
|
|
||||||
func (a *Aug) Interact() (err error) {
|
|
||||||
|
|
||||||
var input string
|
|
||||||
var lexed []string
|
|
||||||
var cmd string
|
|
||||||
var arg string
|
|
||||||
var val string
|
|
||||||
var augVal string
|
|
||||||
var augVals []string
|
|
||||||
var buf *bufio.Reader = bufio.NewReader(os.Stdin)
|
|
||||||
|
|
||||||
fmt.Fprint(os.Stderr, "INTERACTIVE MODE\nCmds: get, getall, match, set, quit\n")
|
|
||||||
breakCmd:
|
|
||||||
for {
|
|
||||||
cmd, arg, val = "", "", ""
|
|
||||||
|
|
||||||
fmt.Fprint(os.Stderr, "> ")
|
|
||||||
if input, err = buf.ReadString('\n'); err != nil {
|
|
||||||
if errors.Is(err, io.EOF) {
|
|
||||||
err = nil
|
|
||||||
break breakCmd
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(input) == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if lexed, err = shlex.Split(input); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if lexed == nil || len(lexed) == 0 || len(lexed) > 3 {
|
|
||||||
fmt.Fprintf(os.Stderr, "Bad command: %#v\n", lexed)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cmd = lexed[0]
|
|
||||||
switch len(lexed) {
|
|
||||||
case 2:
|
|
||||||
arg = lexed[1]
|
|
||||||
case 3:
|
|
||||||
arg = lexed[1]
|
|
||||||
val = lexed[2]
|
|
||||||
}
|
|
||||||
switch cmd {
|
|
||||||
case "get":
|
|
||||||
if strings.TrimSpace(arg) == "" {
|
|
||||||
fmt.Fprintln(os.Stderr, "Missing argument")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if augVal, err = a.aug.Get(arg); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "!! ERROR !!\n%v\n", spew.Sdump(err))
|
|
||||||
err = nil
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Fprintln(os.Stderr, augVal)
|
|
||||||
case "getall":
|
|
||||||
if strings.TrimSpace(arg) == "" {
|
|
||||||
fmt.Fprintln(os.Stderr, "Missing argument")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if augVals, err = a.aug.GetAll(arg); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "!! ERROR !!\n%v\n", spew.Sdump(err))
|
|
||||||
err = nil
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Fprintln(os.Stderr, strings.Join(augVals, "\n"))
|
|
||||||
case "match":
|
|
||||||
if strings.TrimSpace(arg) == "" {
|
|
||||||
fmt.Fprintln(os.Stderr, "Missing argument")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if augVals, err = a.aug.Match(arg); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "!! ERROR !!\n%v\n", spew.Sdump(err))
|
|
||||||
err = nil
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Fprintln(os.Stderr, strings.Join(augVals, "\n"))
|
|
||||||
case "set":
|
|
||||||
if strings.TrimSpace(arg) == "" {
|
|
||||||
fmt.Fprintln(os.Stderr, "Missing argument")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(val) == "" {
|
|
||||||
fmt.Fprintln(os.Stderr, "Missing value")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err = a.aug.Set(arg, val); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "!! ERROR !!\n%v\n", spew.Sdump(err))
|
|
||||||
err = nil
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Fprintln(os.Stderr, "Success!")
|
|
||||||
case "quit":
|
|
||||||
break breakCmd
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(os.Stderr, "Unknown command: %v\n", cmd)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
RecursiveInclude parses the configuration files belonging to Augeas lens name augLens,
|
|
||||||
searching for all occurrences of includeDirective, loading those files (if they exist),
|
|
||||||
and continuing so forth recursively, loading them into the Augeas file tree.
|
|
||||||
|
|
||||||
If any relative paths are found, they will be assumed to be relative to fsRoot ("/" if empty).
|
|
||||||
For e.g. Nginx, you almost absolutely want to set this to "/etc/nginx", but you really should
|
|
||||||
use absolute paths for every include in your configs if supported by the application; it will
|
|
||||||
lead to much less guesswork and much more accurate recursing/walking.
|
|
||||||
|
|
||||||
Some lens recursively load depending on their respective include directive(s) automatically;
|
|
||||||
some (such as the Nginx lens) do not.
|
|
||||||
|
|
||||||
For example for Nginx, augLens should be "Nginx". RecursiveInclude will then iterate over
|
|
||||||
/augeas/load/Nginx/incl (/augeas/load/<augLens>/incl), parsing each file for includeDirective
|
|
||||||
(the "include" keyword, in Nginx's case), check if it is already loaded in /augeas/load/<augLens>/incl,
|
|
||||||
adding it and reloading if not, and then scanning *that* file for includeDirective, etc.
|
|
||||||
|
|
||||||
An error will be returned if augLens is a nonexistent or not-loaded Augeas lens module.
|
|
||||||
|
|
||||||
Depending on how many files there are and whether globs vs. explicit filepaths are included, this may take a while.
|
|
||||||
|
|
||||||
optFlags may be nil, multiple includeOpt (see the IncludeOpt* constants) as variadic parameters/expanded slice,
|
|
||||||
bitwise-OR'd together, or multiple non-OR'd and OR'd together (all will be combined to a single value).
|
|
||||||
*/
|
|
||||||
func (a *Aug) RecursiveInclude(augLens, includeDirective, fsRoot string, optFlags ...includeOpt) (err error) {
|
|
||||||
|
|
||||||
var flags *bitmask.MaskBit = bitmask.NewMaskBit()
|
|
||||||
|
|
||||||
if optFlags != nil && len(optFlags) > 0 {
|
|
||||||
for _, f := range optFlags {
|
|
||||||
flags.AddFlag(f.toMb())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = a.addIncl(includeDirective, augLens, fsRoot, nil, flags); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
addIncl is used by RecursiveInclude.
|
|
||||||
|
|
||||||
includeDirective, augLens, and fsRoot have the same meaning as in RecursiveInclude.
|
|
||||||
|
|
||||||
newInclPaths are new filesystem paths/Augeas-compatible glob patterns to load into the filetree and recurse into.
|
|
||||||
They may be nil, especially if the first run.
|
|
||||||
*/
|
|
||||||
func (a *Aug) addIncl(includeDirective, augLens string, fsRoot string, newInclPaths []string, inclFlags *bitmask.MaskBit) (err error) {
|
|
||||||
|
|
||||||
var matches []string // Passed around set of Augeas matches.
|
|
||||||
var exists bool // Used to indicate if the include path exists.
|
|
||||||
var includes []string // Filepath(s)/glob(s) from fetching includeDirective in lensInclPath. These are internal to the application but are recursed.
|
|
||||||
var lensInclPath string // The path of the included paths in the tree. These are internal to Augeas, not the application.
|
|
||||||
var appendPath string // The path for new Augeas includes.
|
|
||||||
var match []string // A placeholder for iterating when populating includes.
|
|
||||||
var fpath string // A placeholder for finding the path of a conf file that contains an includeDirective.
|
|
||||||
var normalizedIncludes []string // A temporary slice to hold normalization operations and other dynamic building.
|
|
||||||
var lensPath string = fmt.Sprintf(augLensTpl, augLens) // The path of the lens (augLens) itself.
|
|
||||||
var augErr *augeas.Error = new(augeas.Error) // We use this to skip "nonexistent" lens.
|
|
||||||
|
|
||||||
if fsRoot == "" {
|
|
||||||
fsRoot = "/"
|
|
||||||
}
|
|
||||||
if err = paths.RealPath(&fsRoot); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for strings.HasSuffix(lensPath, "/") {
|
|
||||||
lensPath = lensPath[:len(lensPath)-1]
|
|
||||||
}
|
|
||||||
if !strings.HasSuffix(lensPath, "/"+augInclTfm) {
|
|
||||||
lensPath = strings.TrimSuffix(lensPath, "/"+augInclTfm)
|
|
||||||
}
|
|
||||||
lensInclPath = fmt.Sprintf("%v/%v", lensPath, augInclTfm)
|
|
||||||
appendPath = fmt.Sprintf("%v/%v", lensInclPath, augAppendSuffix)
|
|
||||||
|
|
||||||
// First canonize paths.
|
|
||||||
if newInclPaths != nil && len(newInclPaths) > 0 {
|
|
||||||
// Existing includes. We don't return on an empty lensInclPath.
|
|
||||||
if matches, err = a.aug.Match(lensInclPath); err != nil {
|
|
||||||
if errors.As(err, augErr) && augErr.Code == augeas.NoMatch {
|
|
||||||
err = nil
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for idx, m := range matches {
|
|
||||||
if matches[idx], err = a.aug.Get(m); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize new include(s).
|
|
||||||
for idx, i := range newInclPaths {
|
|
||||||
if !filepath.IsAbs(i) {
|
|
||||||
newInclPaths[idx] = filepath.Join(fsRoot, i)
|
|
||||||
}
|
|
||||||
if err = paths.RealPath(&newInclPaths[idx]); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't want to bother adding multiple incl's for the same path(s); it can negatively affect Augeas loads.
|
|
||||||
newInclPaths = dedupePaths(newInclPaths, matches)
|
|
||||||
|
|
||||||
// And then apply things like recursion, globbing, etc.
|
|
||||||
normalizedIncludes = make([]string, 0, len(newInclPaths))
|
|
||||||
if inclFlags.HasFlag(IncludeOptGlobbing.toMb()) {
|
|
||||||
// TODO
|
|
||||||
/*
|
|
||||||
if strings.Contains(newInclPaths[idx], "*") {
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the new path(s) as Augeas include entries.
|
|
||||||
if newInclPaths != nil {
|
|
||||||
for _, fsPath := range newInclPaths {
|
|
||||||
if err = a.aug.Set(appendPath, fsPath); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
And then load the new files into the tree.
|
|
||||||
This is done at the end as it takes WAYYY less time to just reload the tree
|
|
||||||
as a whole once you have more than, say, 30 files added at a time.
|
|
||||||
*/
|
|
||||||
if err = a.aug.Load(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We now fetch all values (filepath/globs) that match the includeDirective and recurse with those as new include files.
|
|
||||||
matches = nil
|
|
||||||
if includes, err = a.aug.GetAll(lensInclPath); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, fsPath := range includes {
|
|
||||||
// This gets the Augeas filetree path, NOT the FS path...
|
|
||||||
if match, err = a.aug.Match(fmt.Sprintf(augFsTpl, fsPath, includeDirective)); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if match == nil || len(match) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
For each directive match, we need to:
|
|
||||||
|
|
||||||
1.) normalize to an FS *file* path (strip augFsTree from the beginning
|
|
||||||
2.) walk backwards (via AugpathToFspath) until we find an actual file
|
|
||||||
3.) get the *dirname* of that file
|
|
||||||
4.) join the value (included file) to #3
|
|
||||||
IF
|
|
||||||
fsRoot == "/"
|
|
||||||
|
|
||||||
This very obviously breaks for applications with arbitrary roots (like Nginx including relative to /etc/nginx).
|
|
||||||
That's why we warn about it in Aug.RecursiveInclude. Caveat emptor.
|
|
||||||
*/
|
|
||||||
for idx, ftreePath := range match {
|
|
||||||
if fpath, err = a.aug.Get(ftreePath); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if fsRoot == "/" {
|
|
||||||
if ftreePath, err = AugpathToFspath(
|
|
||||||
strings.TrimSuffix(ftreePath, augFsTree),
|
|
||||||
); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ftreePath != "" {
|
|
||||||
fpath = filepath.Join(filepath.Dir(ftreePath), fpath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match[idx] = fpath
|
|
||||||
}
|
|
||||||
matches = append(matches, match...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if matches != nil && len(matches) != 0 {
|
|
||||||
if err = a.addIncl(includeDirective, augLens, fsRoot, matches, inclFlags); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
_, _ = exists, normalizedIncludes
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
package auger
|
|
||||||
|
|
||||||
import (
|
|
||||||
`honnef.co/go/augeas`
|
|
||||||
)
|
|
||||||
|
|
||||||
// Eval returns an evaluated set of flags.
|
|
||||||
func (a *AugFlags) Eval() (augFlags augeas.Flag) {
|
|
||||||
|
|
||||||
if a == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
augFlags = augeas.None
|
|
||||||
|
|
||||||
if a.Backup != nil && *a.Backup {
|
|
||||||
augFlags |= augeas.SaveBackup
|
|
||||||
}
|
|
||||||
if a.NewFile != nil && *a.NewFile {
|
|
||||||
augFlags |= augeas.SaveNewFile
|
|
||||||
}
|
|
||||||
if a.TypeCheck != nil && *a.TypeCheck {
|
|
||||||
augFlags |= augeas.TypeCheck
|
|
||||||
}
|
|
||||||
if a.NoDfltModLoad != nil && *a.NoDfltModLoad {
|
|
||||||
augFlags |= augeas.NoModlAutoload
|
|
||||||
}
|
|
||||||
if a.DryRun != nil && *a.DryRun {
|
|
||||||
augFlags |= augeas.SaveNoop
|
|
||||||
}
|
|
||||||
if a.NoTree != nil && *a.NoTree {
|
|
||||||
augFlags |= augeas.NoLoad
|
|
||||||
}
|
|
||||||
if a.NoAutoModLoad != nil && *a.NoAutoModLoad {
|
|
||||||
augFlags |= augeas.NoModlAutoload
|
|
||||||
}
|
|
||||||
if a.EnableSpan != nil && *a.EnableSpan {
|
|
||||||
augFlags |= augeas.EnableSpan
|
|
||||||
}
|
|
||||||
if a.NoErrClose != nil && *a.NoErrClose {
|
|
||||||
augFlags |= augeas.NoErrClose
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package auger
|
|
||||||
|
|
||||||
import (
|
|
||||||
`r00t2.io/goutils/bitmask`
|
|
||||||
)
|
|
||||||
|
|
||||||
// toMb returns a bitmask.MaskBit of this includeOpt.
|
|
||||||
func (i includeOpt) toMb() (mb bitmask.MaskBit) {
|
|
||||||
|
|
||||||
mb = bitmask.MaskBit(i)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package auger
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
`honnef.co/go/augeas`
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewAuger(t *testing.T) {
|
|
||||||
|
|
||||||
var aug *Aug
|
|
||||||
var augUnder augeas.Augeas
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if aug, err = NewAuger("/", "", nil); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
augUnder = aug.aug
|
|
||||||
|
|
||||||
aug = NewAugerFromAugeas(augUnder)
|
|
||||||
|
|
||||||
_ = aug
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRecursiveInclude(t *testing.T) {
|
|
||||||
|
|
||||||
var aug *Aug
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if aug, err = NewAuger("/", "", &AugFlags{DryRun: PtrTrue}); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This requires Nginx to be installed and with a particularly complex nested include system.
|
|
||||||
if err = aug.RecursiveInclude("Nginx", "include", "/etc/nginx"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
package auger
|
|
||||||
|
|
||||||
import (
|
|
||||||
`honnef.co/go/augeas`
|
|
||||||
`r00t2.io/goutils/bitmask`
|
|
||||||
)
|
|
||||||
|
|
||||||
type includeOpt bitmask.MaskBit
|
|
||||||
|
|
||||||
// Aug is a wrapper around (honnef.co/go/)augeas.Augeas. Remember to call Aug.Close().
|
|
||||||
type Aug struct {
|
|
||||||
aug augeas.Augeas
|
|
||||||
}
|
|
||||||
|
|
||||||
// AugFlags contains flags to pass to the Augeas instance.
|
|
||||||
type AugFlags struct {
|
|
||||||
/*
|
|
||||||
Backup, if true, will enable Augeas backup mode (original files are saved with a .augsave suffix).
|
|
||||||
const: augeas.SaveBackup
|
|
||||||
*/
|
|
||||||
Backup *bool `toml:"Backup,omitempty"`
|
|
||||||
/*
|
|
||||||
NewFile, if true, will create new files (.augnew suffix) instead of overwriting the original file.
|
|
||||||
const: augeas.SaveNewFile
|
|
||||||
*/
|
|
||||||
NewFile *bool `toml:"NewFile,omitempty"`
|
|
||||||
/*
|
|
||||||
TypeCheck, if true, will perform a Lens typecheck.
|
|
||||||
HIGHLY UNRECOMMENDED; WILL INDUCE A HUGE FRONTLOAD.
|
|
||||||
const: augeas.TypeCheck
|
|
||||||
*/
|
|
||||||
TypeCheck *bool `toml:"TypeCheck,omitempty"`
|
|
||||||
/*
|
|
||||||
NoDfltModLoad, if true, will suppress loading the built-in/default modules.
|
|
||||||
Highly unrecommended, as we do not have a current way in the config to define load paths (yet).
|
|
||||||
const: augeas.NoStdinc
|
|
||||||
*/
|
|
||||||
NoDfltModLoad *bool `toml:"NoDfltModLoad,omitempty"`
|
|
||||||
/*
|
|
||||||
DryRun, if true, will make all saves NO-OPs.
|
|
||||||
const: augeas.SaveNoop
|
|
||||||
*/
|
|
||||||
DryRun *bool `toml:"DryRun,omitempty"`
|
|
||||||
/*
|
|
||||||
NoTree, if true, will not load the filetree automatically. Doesn't really affect this program.
|
|
||||||
const: augeas.NoLoad
|
|
||||||
*/
|
|
||||||
NoTree *bool `toml:"NoTree,omitempty"`
|
|
||||||
/*
|
|
||||||
NoAutoModLoad, if true, will supress automatically loading modules.
|
|
||||||
const: augeas.NoModlAutoload
|
|
||||||
*/
|
|
||||||
NoAutoModLoad *bool `toml:"NoAutoModLoad,omitempty"`
|
|
||||||
/*
|
|
||||||
EnableSpan, if true, will track span in input nodes (location information, essentially).
|
|
||||||
See https://augeas.net/docs/api.html#getting-the-span-of-a-node-related-to-a-file
|
|
||||||
const: augeas.EnableSpan
|
|
||||||
*/
|
|
||||||
EnableSpan *bool `toml:"EnableSpan,omitempty"`
|
|
||||||
/*
|
|
||||||
NoErrClose, if true, will suppress automatically closing on error.
|
|
||||||
const: augeas.NoErrClose
|
|
||||||
*/
|
|
||||||
NoErrClose *bool `toml:"NoErrClose,omitempty"`
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
/*
|
|
||||||
CRYPTPARSE HAS MOVED.
|
|
||||||
|
|
||||||
It is now its own module: r00t2.io/cryptparse
|
|
||||||
*/
|
|
||||||
package cryptparse
|
|
@ -9,7 +9,3 @@ var (
|
|||||||
reMaybeInt *regexp.Regexp = regexp.MustCompile(`^(?P<sign>\+|-)[0-9]+$`)
|
reMaybeInt *regexp.Regexp = regexp.MustCompile(`^(?P<sign>\+|-)[0-9]+$`)
|
||||||
reMaybeFloat *regexp.Regexp = regexp.MustCompile(`(?P<sign>\+|-)?[0-9]+\.[0-9]+$`)
|
reMaybeFloat *regexp.Regexp = regexp.MustCompile(`(?P<sign>\+|-)?[0-9]+\.[0-9]+$`)
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
StructTagInterpolate string = "envsub"
|
|
||||||
)
|
|
||||||
|
527
envs/funcs.go
527
envs/funcs.go
@ -6,42 +6,25 @@ import (
|
|||||||
`fmt`
|
`fmt`
|
||||||
`io/ioutil`
|
`io/ioutil`
|
||||||
`os`
|
`os`
|
||||||
`reflect`
|
|
||||||
`strings`
|
`strings`
|
||||||
`sync`
|
|
||||||
|
|
||||||
`r00t2.io/goutils/multierr`
|
|
||||||
`r00t2.io/goutils/structutils`
|
|
||||||
`r00t2.io/sysutils/errs`
|
|
||||||
`r00t2.io/sysutils/internal`
|
`r00t2.io/sysutils/internal`
|
||||||
`r00t2.io/sysutils/paths`
|
`r00t2.io/sysutils/paths`
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
// GetPathEnv returns a slice of the PATH variable's items.
|
||||||
DefEnv operates like Python's .get() method on dicts (maps);
|
func GetPathEnv() (pathList []string, err error) {
|
||||||
if the environment variable specified by key does not exist/is not specified,
|
|
||||||
then the value specified by fallback will be returned instead
|
|
||||||
otherwise key's value is returned.
|
|
||||||
*/
|
|
||||||
func DefEnv(key, fallback string) (value string) {
|
|
||||||
|
|
||||||
var exists bool
|
var pathVar string = internal.GetPathEnvName()
|
||||||
|
|
||||||
if value, exists = os.LookupEnv(key); !exists {
|
pathList = make([]string, 0)
|
||||||
value = fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for _, p := range strings.Split(os.Getenv(pathVar), string(os.PathListSeparator)) {
|
||||||
|
if err = paths.RealPath(&p); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
pathList = append(pathList, p)
|
||||||
// DefEnvBlank is like DefEnv but will ADDITIONALLY/ALSO apply fallback if key is *defined/exists but is an empty string*.
|
|
||||||
func DefEnvBlank(key, fallback string) (value string) {
|
|
||||||
|
|
||||||
value = DefEnv(key, fallback)
|
|
||||||
if value == "" {
|
|
||||||
value = fallback
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,75 +61,6 @@ func GetEnvMapNative() (envMap map[string]interface{}) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
GetFirst gets the first instance if populated/set occurrence of varNames.
|
|
||||||
|
|
||||||
For example, if you have three potential env vars, FOO, FOOBAR, FOOBARBAZ,
|
|
||||||
and want to follow the logic flow of:
|
|
||||||
|
|
||||||
1.) Check if FOO is set. If not,
|
|
||||||
2.) Check if FOOBAR is set. If not,
|
|
||||||
3.) Check if FOOBARBAZ is set.
|
|
||||||
|
|
||||||
Then this would be specified as:
|
|
||||||
|
|
||||||
GetFirst([]string{"FOO", "FOOBAR", "FOOBARBAZ"})
|
|
||||||
|
|
||||||
If val is "" and ok is true, this means that one of the specified variable names IS
|
|
||||||
set but is set to an empty value. If ok is false, none of the specified variables
|
|
||||||
are set.
|
|
||||||
|
|
||||||
It is a thin wrapper around GetFirstWithRef.
|
|
||||||
*/
|
|
||||||
func GetFirst(varNames []string) (val string, ok bool) {
|
|
||||||
|
|
||||||
val, ok, _ = GetFirstWithRef(varNames)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
GetFirstWithRef behaves exactly like GetFirst, but with an additional returned value, idx,
|
|
||||||
which specifies the index in varNames in which a set variable was found. e.g. if:
|
|
||||||
|
|
||||||
GetFirstWithRef([]string{"FOO", "FOOBAR", "FOOBAZ"})
|
|
||||||
|
|
||||||
is called and FOO is not set but FOOBAR is, idx will be 1.
|
|
||||||
|
|
||||||
If ok is false, idx will always be -1 and should be ignored.
|
|
||||||
*/
|
|
||||||
func GetFirstWithRef(varNames []string) (val string, ok bool, idx int) {
|
|
||||||
|
|
||||||
idx = -1
|
|
||||||
|
|
||||||
for i, vn := range varNames {
|
|
||||||
if HasEnv(vn) {
|
|
||||||
ok = true
|
|
||||||
idx = i
|
|
||||||
val = os.Getenv(vn)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPathEnv returns a slice of the PATH variable's items.
|
|
||||||
func GetPathEnv() (pathList []string, err error) {
|
|
||||||
|
|
||||||
var pathVar string = internal.GetPathEnvName()
|
|
||||||
|
|
||||||
pathList = make([]string, 0)
|
|
||||||
|
|
||||||
for _, p := range strings.Split(os.Getenv(pathVar), string(os.PathListSeparator)) {
|
|
||||||
if err = paths.RealPath(&p); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pathList = append(pathList, p)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
GetPidEnvMap will only work on *NIX-like systems with procfs.
|
GetPidEnvMap will only work on *NIX-like systems with procfs.
|
||||||
It gets the environment variables of a given process' PID.
|
It gets the environment variables of a given process' PID.
|
||||||
@ -159,7 +73,7 @@ func GetPidEnvMap(pid uint32) (envMap map[string]string, err error) {
|
|||||||
var procPath string
|
var procPath string
|
||||||
var exists bool
|
var exists bool
|
||||||
|
|
||||||
envMap = make(map[string]string)
|
envMap = make(map[string]string, 0)
|
||||||
|
|
||||||
procPath = fmt.Sprintf("/proc/%v/environ", pid)
|
procPath = fmt.Sprintf("/proc/%v/environ", pid)
|
||||||
|
|
||||||
@ -204,428 +118,3 @@ func GetPidEnvMapNative(pid uint32) (envMap map[string]interface{}, err error) {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
HasEnv is much like os.LookupEnv, but only returns a boolean for
|
|
||||||
if the environment variable key exists or not.
|
|
||||||
|
|
||||||
This is useful anywhere you may need to set a boolean in a func call
|
|
||||||
depending on the *presence* of an env var or not.
|
|
||||||
*/
|
|
||||||
func HasEnv(key string) (envIsSet bool) {
|
|
||||||
|
|
||||||
_, envIsSet = os.LookupEnv(key)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Interpolate takes one of:
|
|
||||||
|
|
||||||
- a string (pointer only)
|
|
||||||
- a struct (pointer only)
|
|
||||||
- a map (applied to both keys *and* values)
|
|
||||||
- a slice
|
|
||||||
|
|
||||||
and performs variable substitution on strings from environment variables.
|
|
||||||
|
|
||||||
It supports both UNIX/Linux/POSIX syntax formats (e.g. $VARNAME, ${VARNAME}) and,
|
|
||||||
if on Windows, it *additionally* supports the EXPAND_SZ format (e.g. %VARNAME%).
|
|
||||||
|
|
||||||
For structs, the tag name used can be changed by setting the StructTagInterpolate
|
|
||||||
variable in this submodule; the default is `envsub`.
|
|
||||||
If the tag value is "-", the field will be skipped.
|
|
||||||
For map fields within structs etc., the default is to apply interpolation to both keys and values.
|
|
||||||
All other tag value(s) are ignored.
|
|
||||||
|
|
||||||
For maps and slices, Interpolate will recurse into values (e.g. [][]string will work as expected).
|
|
||||||
|
|
||||||
If s is nil, no interpolation will be performed. No error will be returned.
|
|
||||||
If s is not a valid/supported type, no interpolation will be performed. No error will be returned.
|
|
||||||
*/
|
|
||||||
func Interpolate[T any](s T) (err error) {
|
|
||||||
|
|
||||||
var ptrVal reflect.Value
|
|
||||||
var ptrType reflect.Type
|
|
||||||
var ptrKind reflect.Kind
|
|
||||||
var sVal reflect.Value = reflect.ValueOf(s)
|
|
||||||
var sType reflect.Type = sVal.Type()
|
|
||||||
var kind reflect.Kind = sType.Kind()
|
|
||||||
|
|
||||||
switch kind {
|
|
||||||
case reflect.Ptr:
|
|
||||||
if sVal.IsNil() || sVal.IsZero() || !sVal.IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ptrVal = sVal.Elem()
|
|
||||||
ptrType = ptrVal.Type()
|
|
||||||
ptrKind = ptrType.Kind()
|
|
||||||
if ptrKind == reflect.String {
|
|
||||||
err = interpolateStringReflect(ptrVal)
|
|
||||||
} else {
|
|
||||||
// Otherwise, it should be a struct ptr.
|
|
||||||
if ptrKind != reflect.Struct {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = interpolateStruct(ptrVal)
|
|
||||||
}
|
|
||||||
case reflect.Map:
|
|
||||||
if sVal.IsNil() || sVal.IsZero() || !sVal.IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = interpolateMap(sVal)
|
|
||||||
case reflect.Slice:
|
|
||||||
if sVal.IsNil() || sVal.IsZero() || !sVal.IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = interpolateSlice(sVal)
|
|
||||||
/*
|
|
||||||
case reflect.Struct:
|
|
||||||
if sVal.IsZero() || !sVal.IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = interpolateStruct(sVal)
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
InterpolateString takes (a pointer to) a struct or string and performs variable substitution on it
|
|
||||||
from environment variables.
|
|
||||||
|
|
||||||
It supports both UNIX/Linux/POSIX syntax formats (e.g. $VARNAME, ${VARNAME}) and,
|
|
||||||
if on Windows, it *additionally* supports the EXPAND_SZ format (e.g. %VARNAME%).
|
|
||||||
|
|
||||||
If s is nil, nothing will be done and err will be ErrNilPtr.
|
|
||||||
|
|
||||||
This is a standalone function that is much more performant than Interpolate
|
|
||||||
at the cost of rigidity.
|
|
||||||
*/
|
|
||||||
func InterpolateString(s *string) (err error) {
|
|
||||||
|
|
||||||
var newStr string
|
|
||||||
|
|
||||||
if s == nil {
|
|
||||||
err = errs.ErrNilPtr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if newStr, err = interpolateString(*s); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
*s = newStr
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// interpolateMap is used by Interpolate for maps. v should be a reflect.Value of a map.
|
|
||||||
func interpolateMap(v reflect.Value) (err error) {
|
|
||||||
|
|
||||||
var kVal reflect.Value
|
|
||||||
var vVal reflect.Value
|
|
||||||
var newMap reflect.Value
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
var numJobs int
|
|
||||||
var errChan chan error
|
|
||||||
var doneChan chan bool = make(chan bool, 1)
|
|
||||||
var mErr *multierr.MultiError = multierr.NewMultiError(nil)
|
|
||||||
var t reflect.Type = v.Type()
|
|
||||||
var kind reflect.Kind = t.Kind()
|
|
||||||
|
|
||||||
if kind != reflect.Map {
|
|
||||||
err = errs.ErrBadType
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.IsNil() || v.IsZero() || !v.IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
numJobs = v.Len()
|
|
||||||
errChan = make(chan error, numJobs)
|
|
||||||
wg.Add(numJobs)
|
|
||||||
|
|
||||||
newMap = reflect.MakeMap(v.Type())
|
|
||||||
|
|
||||||
for _, kVal = range v.MapKeys() {
|
|
||||||
vVal = v.MapIndex(kVal)
|
|
||||||
go func(key, val reflect.Value) {
|
|
||||||
var mapErr error
|
|
||||||
var newKey reflect.Value
|
|
||||||
var newVal reflect.Value
|
|
||||||
|
|
||||||
newKey = reflect.New(key.Type()).Elem()
|
|
||||||
newVal = reflect.New(val.Type()).Elem()
|
|
||||||
|
|
||||||
newKey.Set(key.Convert(newKey.Type()))
|
|
||||||
newVal.Set(val.Convert(newVal.Type()))
|
|
||||||
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
// key
|
|
||||||
if key.Kind() == reflect.String {
|
|
||||||
if mapErr = interpolateStringReflect(newKey); mapErr != nil {
|
|
||||||
errChan <- mapErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if mapErr = interpolateValue(newKey); mapErr != nil {
|
|
||||||
errChan <- mapErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// value
|
|
||||||
if val.Kind() == reflect.String {
|
|
||||||
if mapErr = interpolateStringReflect(newVal); mapErr != nil {
|
|
||||||
errChan <- mapErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if mapErr = interpolateValue(newVal); mapErr != nil {
|
|
||||||
errChan <- mapErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newMap.SetMapIndex(newKey.Convert(key.Type()), newVal.Convert(key.Type()))
|
|
||||||
}(kVal, vVal)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
wg.Wait()
|
|
||||||
close(errChan)
|
|
||||||
doneChan <- true
|
|
||||||
}()
|
|
||||||
|
|
||||||
<-doneChan
|
|
||||||
|
|
||||||
for i := 0; i < numJobs; i++ {
|
|
||||||
if err = <-errChan; err != nil {
|
|
||||||
mErr.AddError(err)
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !mErr.IsEmpty() {
|
|
||||||
err = mErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
v.Set(newMap.Convert(v.Type()))
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// interpolateSlice is used by Interpolate for slices and arrays. v should be a reflect.Value of a slice/array.
|
|
||||||
func interpolateSlice(v reflect.Value) (err error) {
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
var errChan chan error
|
|
||||||
var numJobs int
|
|
||||||
var doneChan chan bool = make(chan bool, 1)
|
|
||||||
var mErr *multierr.MultiError = multierr.NewMultiError(nil)
|
|
||||||
var t reflect.Type = v.Type()
|
|
||||||
var kind reflect.Kind = t.Kind()
|
|
||||||
|
|
||||||
switch kind {
|
|
||||||
case reflect.Slice:
|
|
||||||
if v.IsNil() || v.IsZero() || !v.IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case reflect.Array:
|
|
||||||
if v.IsZero() || !v.IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
err = errs.ErrBadType
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
numJobs = v.Len()
|
|
||||||
errChan = make(chan error, numJobs)
|
|
||||||
wg.Add(numJobs)
|
|
||||||
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
go func(idx int) {
|
|
||||||
var sErr error
|
|
||||||
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
if v.Index(idx).Kind() == reflect.String {
|
|
||||||
if sErr = interpolateStringReflect(v.Index(idx)); sErr != nil {
|
|
||||||
errChan <- sErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if sErr = interpolateValue(v.Index(idx)); sErr != nil {
|
|
||||||
errChan <- sErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
wg.Wait()
|
|
||||||
close(errChan)
|
|
||||||
doneChan <- true
|
|
||||||
}()
|
|
||||||
|
|
||||||
<-doneChan
|
|
||||||
|
|
||||||
for i := 0; i < numJobs; i++ {
|
|
||||||
if err = <-errChan; err != nil {
|
|
||||||
mErr.AddError(err)
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !mErr.IsEmpty() {
|
|
||||||
err = mErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// interpolateStringReflect is used for structs/nested strings using reflection.
|
|
||||||
func interpolateStringReflect(v reflect.Value) (err error) {
|
|
||||||
|
|
||||||
var strVal string
|
|
||||||
|
|
||||||
if v.Kind() != reflect.String {
|
|
||||||
err = errs.ErrBadType
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if strVal, err = interpolateString(v.String()); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
v.Set(reflect.ValueOf(strVal).Convert(v.Type()))
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// interpolateStruct is used by Interpolate for structs. v should be a reflect.Value of a struct.
|
|
||||||
func interpolateStruct(v reflect.Value) (err error) {
|
|
||||||
|
|
||||||
var field reflect.StructField
|
|
||||||
var fieldVal reflect.Value
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
var errChan chan error
|
|
||||||
var numJobs int
|
|
||||||
var doneChan chan bool = make(chan bool, 1)
|
|
||||||
var mErr *multierr.MultiError = multierr.NewMultiError(nil)
|
|
||||||
var t reflect.Type = v.Type()
|
|
||||||
var kind reflect.Kind = t.Kind()
|
|
||||||
|
|
||||||
if kind != reflect.Struct {
|
|
||||||
err = errs.ErrBadType
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
numJobs = v.NumField()
|
|
||||||
wg.Add(numJobs)
|
|
||||||
errChan = make(chan error, numJobs)
|
|
||||||
|
|
||||||
for i := 0; i < v.NumField(); i++ {
|
|
||||||
field = t.Field(i)
|
|
||||||
fieldVal = v.Field(i)
|
|
||||||
|
|
||||||
go func(f reflect.StructField, fv reflect.Value) {
|
|
||||||
var fErr error
|
|
||||||
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
if fErr = interpolateStructField(f, fv); fErr != nil {
|
|
||||||
errChan <- fErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}(field, fieldVal)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
wg.Wait()
|
|
||||||
close(errChan)
|
|
||||||
doneChan <- true
|
|
||||||
}()
|
|
||||||
|
|
||||||
<-doneChan
|
|
||||||
|
|
||||||
for i := 0; i < numJobs; i++ {
|
|
||||||
if err = <-errChan; err != nil {
|
|
||||||
mErr.AddError(err)
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !mErr.IsEmpty() {
|
|
||||||
err = mErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// interpolateStructField interpolates a struct field.
|
|
||||||
func interpolateStructField(field reflect.StructField, v reflect.Value) (err error) {
|
|
||||||
|
|
||||||
var parsedTagOpts map[string]bool
|
|
||||||
|
|
||||||
if !v.CanSet() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip if explicitly instructed to do so.
|
|
||||||
parsedTagOpts = structutils.TagToBoolMap(field, StructTagInterpolate, structutils.TagMapTrim)
|
|
||||||
if parsedTagOpts["-"] {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.Kind() == reflect.Ptr {
|
|
||||||
err = interpolateStructField(field, v.Elem())
|
|
||||||
} else {
|
|
||||||
err = interpolateValue(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// interpolateValue is a dispatcher for a reflect value.
|
|
||||||
func interpolateValue(v reflect.Value) (err error) {
|
|
||||||
|
|
||||||
var kind reflect.Kind = v.Kind()
|
|
||||||
|
|
||||||
switch kind {
|
|
||||||
case reflect.Ptr:
|
|
||||||
if v.IsNil() || v.IsZero() || !v.IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
v = v.Elem()
|
|
||||||
if err = interpolateValue(v); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case reflect.String:
|
|
||||||
if err = interpolateStringReflect(v); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
case reflect.Slice, reflect.Array:
|
|
||||||
if err = interpolateSlice(v); err != nil {
|
|
||||||
}
|
|
||||||
case reflect.Map:
|
|
||||||
if err = interpolateMap(v); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case reflect.Struct:
|
|
||||||
if err = interpolateStruct(v); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
@ -1,115 +0,0 @@
|
|||||||
package envs
|
|
||||||
|
|
||||||
import (
|
|
||||||
`os`
|
|
||||||
`testing`
|
|
||||||
`time`
|
|
||||||
|
|
||||||
`github.com/davecgh/go-spew/spew`
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
testCustom string
|
|
||||||
testStruct struct {
|
|
||||||
Hello string
|
|
||||||
HelloPtr *string
|
|
||||||
HelloForce string
|
|
||||||
HelloPtrForce *string
|
|
||||||
HelloNo string `envsub:"-" envpop:"-"`
|
|
||||||
HelloNoPtr *string `envsub:"-" envpop:"-"`
|
|
||||||
BadType int
|
|
||||||
NilField *string
|
|
||||||
NilField2 *string
|
|
||||||
PtrInt *int
|
|
||||||
Custom testCustom
|
|
||||||
MapStr map[string]string
|
|
||||||
SliceStr []string
|
|
||||||
SliceSlice [][]string
|
|
||||||
SliceMap []map[string]string
|
|
||||||
SliceStruct []*testStruct
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestInterpolateString(t *testing.T) {
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var s string = "My username is ${USER}; hello!"
|
|
||||||
var sp *string = &s
|
|
||||||
|
|
||||||
if err = InterpolateString(&s); err != nil {
|
|
||||||
t.Fatalf("Failed interpolation: %v", err)
|
|
||||||
}
|
|
||||||
t.Logf("String test passed:\n%v", s)
|
|
||||||
|
|
||||||
if err = InterpolateString(sp); err != nil {
|
|
||||||
t.Fatalf("Failed interpolation: %v", err)
|
|
||||||
}
|
|
||||||
t.Logf("String pointer test passed:\n%v", *sp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInterpolateStruct(t *testing.T) {
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
for _, i := range []interface{}{
|
|
||||||
"i am ${USER}, it is ${CURDATETIME}",
|
|
||||||
new(string),
|
|
||||||
&testStruct{
|
|
||||||
Hello: "i am ${USER}, it is ${CURDATETIME}",
|
|
||||||
HelloPtr: new(string),
|
|
||||||
HelloForce: "i am ${USER}, it is ${CURDATETIME}",
|
|
||||||
HelloPtrForce: new(string),
|
|
||||||
HelloNo: "i am ${USER}, it is ${CURDATETIME}",
|
|
||||||
HelloNoPtr: new(string),
|
|
||||||
BadType: 4,
|
|
||||||
NilField: nil,
|
|
||||||
PtrInt: new(int),
|
|
||||||
Custom: testCustom("i am ${USER}, it is ${CURDATETIME}"),
|
|
||||||
MapStr: map[string]string{"i am ${USER} key": "i am ${USER} value, it is ${CURDATETIME}"},
|
|
||||||
SliceStr: []string{"i am ${USER}, it is ${CURDATETIME}"},
|
|
||||||
SliceSlice: [][]string{[]string{"i am ${USER}, it is ${CURDATETIME}"}},
|
|
||||||
SliceMap: []map[string]string{map[string]string{"i am ${USER} key": "i am ${USER} value, it is ${CURDATETIME}"}},
|
|
||||||
SliceStruct: []*testStruct{
|
|
||||||
&testStruct{
|
|
||||||
Hello: "i am nested ${USER}, it is ${CURDATETIME}",
|
|
||||||
HelloPtr: nil,
|
|
||||||
HelloForce: "i am nested ${USER}, it is ${CURDATETIME}",
|
|
||||||
HelloPtrForce: nil,
|
|
||||||
HelloNo: "i am nested ${USER}, it is ${CURDATETIME}",
|
|
||||||
HelloNoPtr: nil,
|
|
||||||
BadType: 0,
|
|
||||||
NilField: nil,
|
|
||||||
PtrInt: nil,
|
|
||||||
Custom: testCustom("i am nested ${USER}, it is ${CURDATETIME}"),
|
|
||||||
SliceStr: []string{"i am nested ${USER}, it is ${CURDATETIME}"},
|
|
||||||
SliceSlice: [][]string{[]string{"i am nested ${USER}, it is ${CURDATETIME}"}},
|
|
||||||
SliceMap: []map[string]string{map[string]string{"i am nested ${USER} key": "i am ${USER} value, it is ${CURDATETIME}"}},
|
|
||||||
SliceStruct: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
if err = os.Setenv("CURDATETIME", time.Now().String()); err != nil {
|
|
||||||
t.Fatalf("Received error setting CURDATETIME: %v", err)
|
|
||||||
}
|
|
||||||
switch x := i.(type) {
|
|
||||||
case *string:
|
|
||||||
*x = "i am ${USER}, it is ${CURDATETIME}"
|
|
||||||
case testStruct:
|
|
||||||
*x.HelloPtr = x.Hello
|
|
||||||
*x.HelloPtrForce = x.HelloForce
|
|
||||||
*x.HelloNoPtr = x.HelloNo
|
|
||||||
*x.PtrInt = x.BadType
|
|
||||||
case *testStruct:
|
|
||||||
*x.HelloPtr = x.Hello
|
|
||||||
*x.HelloPtrForce = x.HelloForce
|
|
||||||
*x.HelloNoPtr = x.HelloNo
|
|
||||||
*x.PtrInt = x.BadType
|
|
||||||
}
|
|
||||||
t.Logf("Before (%T):\n%v", i, spew.Sdump(i))
|
|
||||||
if err = Interpolate(i); err != nil {
|
|
||||||
t.Fatalf("Failed interpolation: %v", err)
|
|
||||||
}
|
|
||||||
t.Logf("After (%T):\n%v\n", i, spew.Sdump(i))
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,7 +13,7 @@ func envListToMap(envs []string) (envMap map[string]string) {
|
|||||||
var kv []string
|
var kv []string
|
||||||
var k, v string
|
var k, v string
|
||||||
|
|
||||||
envMap = make(map[string]string)
|
envMap = make(map[string]string, 0)
|
||||||
|
|
||||||
for _, ev := range envs {
|
for _, ev := range envs {
|
||||||
kv = strings.SplitN(ev, "=", 2)
|
kv = strings.SplitN(ev, "=", 2)
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
//go:build !windows
|
|
||||||
|
|
||||||
package envs
|
|
||||||
|
|
||||||
import (
|
|
||||||
`os`
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
interpolateString takes string s and performs environment variable interpolation/substitution on it.
|
|
||||||
err will always be nil; it's here for compat with the Windows equivalent.
|
|
||||||
*/
|
|
||||||
func interpolateString(s string) (subbed string, err error) {
|
|
||||||
|
|
||||||
subbed = os.ExpandEnv(s)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
//go:build windows
|
|
||||||
|
|
||||||
package envs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows/registry"
|
|
||||||
)
|
|
||||||
|
|
||||||
// interpolateString takes string s and performs environment variable interpolation/substitution on it.
|
|
||||||
func interpolateString(s string) (subbed string, err error) {
|
|
||||||
|
|
||||||
subbed, err = registry.ExpandString(os.ExpandEnv(s))
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
10
errs/errs.go
10
errs/errs.go
@ -1,10 +0,0 @@
|
|||||||
package errs
|
|
||||||
|
|
||||||
import (
|
|
||||||
`errors`
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrBadType error = errors.New("a bad type was passed")
|
|
||||||
ErrNilPtr error = errors.New("a nil pointer was passed")
|
|
||||||
)
|
|
@ -1,2 +0,0 @@
|
|||||||
- for GetCmdFromStruct, support []byte fields
|
|
||||||
-- support hex and base64 struct field opts (and others?) via `enc=` struct tag.
|
|
@ -1,123 +0,0 @@
|
|||||||
package exec_extra
|
|
||||||
|
|
||||||
import (
|
|
||||||
`r00t2.io/goutils/bitmask`
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
CmdArgsTag string = "cmdarg"
|
|
||||||
/*
|
|
||||||
CmdArgsDictSep specifies the string to use to separate keys and values.
|
|
||||||
|
|
||||||
To override at the struct field level, use the tag value:
|
|
||||||
|
|
||||||
`<CmdArgsTag>:"dictsep=<str>"`
|
|
||||||
|
|
||||||
Where str is the string to use. e.g.:
|
|
||||||
|
|
||||||
`cmdarg:"short=d,long=data,dictsep=."`
|
|
||||||
|
|
||||||
Would render a map value of map[string]string{"foo": "bar"} as:
|
|
||||||
|
|
||||||
`-d foo.bar`
|
|
||||||
*/
|
|
||||||
CmdArgsDictSep string = ":"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CmdArgOptNone is an "empty option" and does nothing.
|
|
||||||
const CmdArgOptNone bitmask.MaskBit = 0
|
|
||||||
const (
|
|
||||||
/*
|
|
||||||
CmdArgOptPreferShort prefers short options where possible.
|
|
||||||
Has no effect if Windows traditional syntax is used.
|
|
||||||
|
|
||||||
The default is to use long options.
|
|
||||||
See also CmdArgOptPreferLong.
|
|
||||||
|
|
||||||
Corresponding struct tag option: prefer_short
|
|
||||||
*/
|
|
||||||
CmdArgOptPreferShort cmdArgOpt = 1 << iota
|
|
||||||
/*
|
|
||||||
CmdArgOptPreferLong prefers long options where possible.
|
|
||||||
Has no effect if Windows traditional syntax is used.
|
|
||||||
|
|
||||||
This behavior is the default, but it can be used to
|
|
||||||
override a CmdArgOptPreferShort from a parent.
|
|
||||||
|
|
||||||
Corresponding struct tag option: prefer_long
|
|
||||||
*/
|
|
||||||
CmdArgOptPreferLong
|
|
||||||
/*
|
|
||||||
CmdArgOptShortEquals will use an equals separator
|
|
||||||
for short flags instead of a space (the default).
|
|
||||||
Has no effect if Windows traditional syntax is used.
|
|
||||||
|
|
||||||
Corresponding struct tag option: short_equals
|
|
||||||
*/
|
|
||||||
CmdArgOptShortEquals
|
|
||||||
/*
|
|
||||||
CmdArgOptShortNoEquals will use a space separator
|
|
||||||
for short flags instead of an equals.
|
|
||||||
Has no effect if Windows traditional syntax is used.
|
|
||||||
|
|
||||||
This behavior is the default, but it can be used to
|
|
||||||
override a CmdArgOptPreferShort from a parent.
|
|
||||||
|
|
||||||
Corresponding struct tag option: no_short_equals
|
|
||||||
*/
|
|
||||||
CmdArgOptShortNoEquals
|
|
||||||
/*
|
|
||||||
CmdArgOptLongEquals will use an equals separator
|
|
||||||
for long flags instead of a space.
|
|
||||||
Has no effect if Windows traditional syntax is used.
|
|
||||||
|
|
||||||
This behavior is the default, but it can be used to
|
|
||||||
override a CmdArgOptLongNoEquals from a parent.
|
|
||||||
|
|
||||||
Corresponding struct tag option: long_equals
|
|
||||||
*/
|
|
||||||
CmdArgOptLongEquals
|
|
||||||
/*
|
|
||||||
CmdArgOptLongNoEquals will use a space separator
|
|
||||||
for short flags instead of an equals.
|
|
||||||
Has no effect if Windows traditional syntax is used.
|
|
||||||
|
|
||||||
This behavior is the default, but it can be used to
|
|
||||||
override a CmdArgOptPreferShort from a parent.
|
|
||||||
|
|
||||||
Corresponding struct tag option: no_long_equals
|
|
||||||
*/
|
|
||||||
CmdArgOptLongNoEquals
|
|
||||||
/*
|
|
||||||
CmdArgOptForceNoPosix forces the resulting command string to use "traditional Windows" flag notation.
|
|
||||||
|
|
||||||
Traditionally, Windows used flags like `/f` instead of POSIX `-f`, `/c:value` instead of `-c value`
|
|
||||||
or `-c=value`, etc.
|
|
||||||
Has no effect if not running on Windows.
|
|
||||||
|
|
||||||
This behavior is the default, but it can be used to
|
|
||||||
override a CmdArgOptPreferShort from a parent.
|
|
||||||
|
|
||||||
See also the inverse of this option, CmdArgOptForcePosix.
|
|
||||||
|
|
||||||
Corresponding struct tag option: force_no_posix
|
|
||||||
*/
|
|
||||||
CmdArgOptForceNoPosix
|
|
||||||
/*
|
|
||||||
CmdArgOptForcePosix forces the resulting command string to use "POSIX" flag notation.
|
|
||||||
|
|
||||||
Traditionally, Windows used flags like `/f` instead of POSIX `-f`, `/c:value` instead of `-c value`
|
|
||||||
or `-c=value`, etc.
|
|
||||||
|
|
||||||
If this option is passed, then the POSIX flag syntax (-a/--arg) will be used instead.
|
|
||||||
|
|
||||||
Note that on Windows runtime, the default is to use the traditional slash-based syntax.
|
|
||||||
If you are generating command strings for Powershell or third-party software, you probably
|
|
||||||
want to use CmdArgsOptForcePosix instead.
|
|
||||||
|
|
||||||
See also the inverse of this option, CmdArgsOptForceNoPosix.
|
|
||||||
|
|
||||||
Corresponding struct tag option: force_posix
|
|
||||||
*/
|
|
||||||
CmdArgOptForcePosix
|
|
||||||
)
|
|
31
exec_extra/func.go
Normal file
31
exec_extra/func.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
SysUtils - a library to assist with various system-related functions
|
||||||
|
Copyright (C) 2020 Brent Saner
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package exec_extra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExecCmdReturn(cmd *exec.Cmd) (exitStatus int, err error) {
|
||||||
|
// https://stackoverflow.com/a/55055100/733214
|
||||||
|
err = cmd.Run()
|
||||||
|
exitErr, _ := err.(*exec.ExitError)
|
||||||
|
exitStatus = exitErr.ExitCode()
|
||||||
|
return
|
||||||
|
}
|
@ -1,355 +0,0 @@
|
|||||||
/*
|
|
||||||
SysUtils - a library to assist with various system-related functions
|
|
||||||
Copyright (C) 2020 Brent Saner
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package exec_extra
|
|
||||||
|
|
||||||
import (
|
|
||||||
`fmt`
|
|
||||||
`os/exec`
|
|
||||||
`reflect`
|
|
||||||
|
|
||||||
`r00t2.io/goutils/bitmask`
|
|
||||||
`r00t2.io/goutils/structutils`
|
|
||||||
`r00t2.io/sysutils/errs`
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
ExecCmdReturn runs cmd and alsom returns the exitStatus.
|
|
||||||
|
|
||||||
A non-zero exit status is not treated as an error.
|
|
||||||
*/
|
|
||||||
func ExecCmdReturn(cmd *exec.Cmd) (exitStatus int, err error) {
|
|
||||||
// https://stackoverflow.com/a/55055100/733214
|
|
||||||
err = cmd.Run()
|
|
||||||
exitErr, _ := err.(*exec.ExitError)
|
|
||||||
exitStatus = exitErr.ExitCode()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
GetCmdFromStruct takes a pointer to a struct and returns a slice of
|
|
||||||
strings compatible with os/exec.Cmd.
|
|
||||||
|
|
||||||
The tag name used can be changed by setting the CmdArgsTag variable in this module;
|
|
||||||
the default is `cmdarg`.
|
|
||||||
|
|
||||||
Tag value format:
|
|
||||||
<tag>:"<option>=<value>[,<option>[=<value>],<option>[=<value>]...]"
|
|
||||||
e.g.
|
|
||||||
cmdarg:"short=l,long=list"
|
|
||||||
cmdarg:"short=l"
|
|
||||||
cmdarg:"long=list"
|
|
||||||
|
|
||||||
If the tag value is "-", the field will be explicitly skipped.
|
|
||||||
(This is the default behavior for struct fields not tagged with `cmdarg`.)
|
|
||||||
If the field is nil, it will be skipped.
|
|
||||||
|
|
||||||
If a cmdarg tag is specified but has no `short` or `long` option value, the field will be skipped entirely.
|
|
||||||
If a field's value is nil, it will be skipped.
|
|
||||||
Otherwise if a field's value is the zero-value, it will be skipped.
|
|
||||||
|
|
||||||
Aside from the 'short' and 'long' tag valued-options, see the comment for each CmdArgOpt* constant
|
|
||||||
for their corresponding tag option and the CmdArgs* variables as well for their corresponding tag option.
|
|
||||||
|
|
||||||
Each struct field can be one of the following types:
|
|
||||||
|
|
||||||
* string
|
|
||||||
* *string
|
|
||||||
* slice (with elements of supported types)
|
|
||||||
* array (with elements of supported types)
|
|
||||||
* map (with keys and values of supported types; see the CmdArgsDictSep variable for the separator to use)
|
|
||||||
* struct (with fields of supported types)
|
|
||||||
* int/int8/int16/int32/int64
|
|
||||||
* uint/uint8/uint16/uint32/uint64
|
|
||||||
* float32/float64
|
|
||||||
|
|
||||||
Struct fields, slice/array elements, etc. are processed in order.
|
|
||||||
Maps, because ordering is non-deterministic, may have unpredictable ordering.
|
|
||||||
|
|
||||||
If s is nil, nothing will be done.
|
|
||||||
If s is not a pointer to a struct, nothing will be done.
|
|
||||||
*/
|
|
||||||
func GetCmdFromStruct[T any](s T, defaultOpts ...cmdArgOpt) (cmdSlice []string, err error) {
|
|
||||||
|
|
||||||
var tmpSlice []string
|
|
||||||
var ptrVal reflect.Value
|
|
||||||
var ptrType reflect.Type
|
|
||||||
var ptrKind reflect.Kind
|
|
||||||
var argFlags *cmdArgFlag
|
|
||||||
var opts *bitmask.MaskBit = bitmask.NewMaskBit()
|
|
||||||
var sVal reflect.Value = reflect.ValueOf(s)
|
|
||||||
var sType reflect.Type = sVal.Type()
|
|
||||||
var kind reflect.Kind = sType.Kind()
|
|
||||||
|
|
||||||
if kind != reflect.Ptr {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if sVal.IsNil() || sVal.IsZero() || !sVal.IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ptrVal = sVal.Elem()
|
|
||||||
ptrType = ptrVal.Type()
|
|
||||||
ptrKind = ptrType.Kind()
|
|
||||||
|
|
||||||
if ptrKind != reflect.Struct {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpSlice = make([]string, 0)
|
|
||||||
if defaultOpts != nil && len(defaultOpts) != 0 {
|
|
||||||
for _, o := range defaultOpts {
|
|
||||||
opts.AddFlag(o.BitMask())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
argFlags = &cmdArgFlag{
|
|
||||||
defaults: new(bitmask.MaskBit),
|
|
||||||
fieldOpts: new(bitmask.MaskBit),
|
|
||||||
boolMap: nil,
|
|
||||||
strMap: nil,
|
|
||||||
shortFlag: "",
|
|
||||||
longFlag: "",
|
|
||||||
field: nil,
|
|
||||||
value: &ptrVal,
|
|
||||||
argSlice: &tmpSlice,
|
|
||||||
}
|
|
||||||
*argFlags.defaults = *opts
|
|
||||||
*argFlags.fieldOpts = *opts
|
|
||||||
|
|
||||||
err = getCmdStruct(argFlags)
|
|
||||||
|
|
||||||
cmdSlice = tmpSlice
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// getCmdStruct iterates over each field of reflect.Value struct v, and is called by GetCmdFromStruct.
|
|
||||||
func getCmdStruct(argFlags *cmdArgFlag) (err error) {
|
|
||||||
|
|
||||||
var t reflect.Type
|
|
||||||
var kind reflect.Kind
|
|
||||||
var fieldArgFlag *cmdArgFlag
|
|
||||||
|
|
||||||
if argFlags == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if argFlags.value == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t = argFlags.value.Type()
|
|
||||||
kind = t.Kind()
|
|
||||||
|
|
||||||
if kind != reflect.Struct {
|
|
||||||
err = errs.ErrBadType
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < argFlags.value.NumField(); i++ {
|
|
||||||
fieldArgFlag = new(cmdArgFlag)
|
|
||||||
*fieldArgFlag = *argFlags
|
|
||||||
fieldArgFlag.field = new(reflect.StructField)
|
|
||||||
fieldArgFlag.value = new(reflect.Value)
|
|
||||||
*fieldArgFlag.field = t.Field(i)
|
|
||||||
*fieldArgFlag.value = argFlags.value.Field(i)
|
|
||||||
|
|
||||||
if err = getCmdStructField(fieldArgFlag); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// getCmdStructField parses an individual struct field.
|
|
||||||
func getCmdStructField(argFlags *cmdArgFlag) (err error) {
|
|
||||||
|
|
||||||
if argFlags == nil || argFlags.field == nil || argFlags.value == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
argFlags.boolMap = structutils.TagToBoolMap(*argFlags.field, CmdArgsTag, structutils.TagMapTrim)
|
|
||||||
if argFlags.boolMap["-"] {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
argFlags.strMap = structutils.TagToStringMap(*argFlags.field, CmdArgsTag, structutils.TagMapTrim)
|
|
||||||
if argFlags.strMap == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for key, val := range argFlags.strMap {
|
|
||||||
switch key {
|
|
||||||
case "short":
|
|
||||||
argFlags.shortFlag = val
|
|
||||||
case "long":
|
|
||||||
argFlags.longFlag = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(argFlags.field.Name + ":")
|
|
||||||
fmt.Printf("BEFORE: %d\t%d\n", argFlags.defaults.Value(), argFlags.fieldOpts.Value())
|
|
||||||
argFlags.fieldOpts = parseCmdArgOpts(argFlags.fieldOpts, argFlags.defaults, *argFlags.field)
|
|
||||||
fmt.Printf("AFTER: %d\t%d\n\n", argFlags.defaults.Value(), argFlags.fieldOpts.Value())
|
|
||||||
/*
|
|
||||||
if v.Kind() == reflect.Ptr {
|
|
||||||
if v.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = getCmdStructField(field, v.Elem(), current, defaults, tmpSlice)
|
|
||||||
} else {
|
|
||||||
err = getCmdValue(v, opts, tagVals, tmpSlice)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// getCmdValue is a dispatcher for a reflect value.
|
|
||||||
func getCmdValue(v reflect.Value, opts *bitmask.MaskBit, flagVals map[string]string, tmpSlice *[]string) (err error) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
var kind reflect.Kind = v.Kind()
|
|
||||||
|
|
||||||
switch kind {
|
|
||||||
case reflect.Ptr:
|
|
||||||
if v.IsNil() || v.IsZero() || !v.IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
v = v.Elem()
|
|
||||||
if err = getCmdValue(v, opts, tmpSlice); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case reflect.String:
|
|
||||||
if err = getCmdString(v, opts, tmpSlice); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
case reflect.Slice, reflect.Array:
|
|
||||||
if err = getCmdSlice(v); err != nil {
|
|
||||||
}
|
|
||||||
case reflect.Map:
|
|
||||||
if err = getCmdMap(v); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case reflect.Struct:
|
|
||||||
if err = getCmdStruct(v); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseCmdArgOpts returns a parsed, combined, etc. set of options into a single OR'd bitmask.
|
|
||||||
func parseCmdArgOpts(current *bitmask.MaskBit, defaults *bitmask.MaskBit, field reflect.StructField) (opts *bitmask.MaskBit) {
|
|
||||||
|
|
||||||
var tagOpts *bitmask.MaskBit = tagOptsToMask(field)
|
|
||||||
|
|
||||||
opts = defaults.Copy()
|
|
||||||
fmt.Printf(
|
|
||||||
"PARSE BEFORE:\n\tOPTS:\t%d\n\tCURRENT:\t%d\n\tDEFAULTS:\t%d\n\tTAGOPTS:\t%d\n",
|
|
||||||
opts.Value(),
|
|
||||||
)
|
|
||||||
for _, b := range []*bitmask.MaskBit{
|
|
||||||
current,
|
|
||||||
tagOpts,
|
|
||||||
} {
|
|
||||||
if b == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if b.HasFlag(CmdArgOptPreferShort.BitMask()) && !b.HasFlag(CmdArgOptPreferLong.BitMask()) {
|
|
||||||
opts.AddFlag(CmdArgOptPreferShort.BitMask())
|
|
||||||
opts.ClearFlag(CmdArgOptPreferLong.BitMask())
|
|
||||||
} else {
|
|
||||||
opts.AddFlag(CmdArgOptPreferLong.BitMask())
|
|
||||||
opts.ClearFlag(CmdArgOptPreferShort.BitMask())
|
|
||||||
}
|
|
||||||
if b.HasFlag(CmdArgOptShortEquals.BitMask()) && !b.HasFlag(CmdArgOptShortNoEquals.BitMask()) {
|
|
||||||
opts.AddFlag(CmdArgOptShortEquals.BitMask())
|
|
||||||
opts.ClearFlag(CmdArgOptShortNoEquals.BitMask())
|
|
||||||
} else {
|
|
||||||
opts.AddFlag(CmdArgOptShortNoEquals.BitMask())
|
|
||||||
opts.ClearFlag(CmdArgOptShortEquals.BitMask())
|
|
||||||
}
|
|
||||||
if b.HasFlag(CmdArgOptLongNoEquals.BitMask()) && !b.HasFlag(CmdArgOptLongEquals.BitMask()) {
|
|
||||||
opts.AddFlag(CmdArgOptLongNoEquals.BitMask())
|
|
||||||
opts.ClearFlag(CmdArgOptLongEquals.BitMask())
|
|
||||||
} else {
|
|
||||||
opts.AddFlag(CmdArgOptLongEquals.BitMask())
|
|
||||||
opts.ClearFlag(CmdArgOptLongNoEquals.BitMask())
|
|
||||||
}
|
|
||||||
if b.HasFlag(CmdArgOptForcePosix.BitMask()) && !b.HasFlag(CmdArgOptForceNoPosix.BitMask()) {
|
|
||||||
opts.AddFlag(CmdArgOptForcePosix.BitMask())
|
|
||||||
opts.ClearFlag(CmdArgOptForceNoPosix.BitMask())
|
|
||||||
} else {
|
|
||||||
opts.AddFlag(CmdArgOptForceNoPosix.BitMask())
|
|
||||||
opts.ClearFlag(CmdArgOptForcePosix.BitMask())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Printf("PARSE AFTER: %d\n", opts.Value())
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// tagOptsToMask returns a bitmask.MaskBit from a struct field's tags.
|
|
||||||
func tagOptsToMask(field reflect.StructField) (b *bitmask.MaskBit) {
|
|
||||||
|
|
||||||
var o cmdArgOpt
|
|
||||||
var tagOpts map[string]bool = structutils.TagToBoolMap(field, CmdArgsTag, structutils.TagMapTrim)
|
|
||||||
|
|
||||||
b = bitmask.NewMaskBit()
|
|
||||||
|
|
||||||
// First round, these are normally disabled.
|
|
||||||
for k, v := range tagOpts {
|
|
||||||
switch k {
|
|
||||||
case "prefer_short":
|
|
||||||
o = CmdArgOptPreferShort
|
|
||||||
case "short_equals":
|
|
||||||
o = CmdArgOptShortEquals
|
|
||||||
case "no_long_equals":
|
|
||||||
o = CmdArgOptLongNoEquals
|
|
||||||
case "force_posix":
|
|
||||||
o = CmdArgOptForcePosix
|
|
||||||
}
|
|
||||||
if v {
|
|
||||||
b.AddFlag(o.BitMask())
|
|
||||||
} else {
|
|
||||||
b.ClearFlag(o.BitMask())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Second round, these override the above.
|
|
||||||
for k, v := range tagOpts {
|
|
||||||
switch k {
|
|
||||||
case "prefer_long":
|
|
||||||
o = CmdArgOptPreferShort
|
|
||||||
case "no_short_equals":
|
|
||||||
o = CmdArgOptShortEquals
|
|
||||||
case "long_equals":
|
|
||||||
o = CmdArgOptLongNoEquals
|
|
||||||
case "force_no_posix":
|
|
||||||
o = CmdArgOptForcePosix
|
|
||||||
}
|
|
||||||
// Since these are meant to disable, we flip things around.
|
|
||||||
if v {
|
|
||||||
b.ClearFlag(o.BitMask())
|
|
||||||
} else {
|
|
||||||
b.AddFlag(o.BitMask())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package exec_extra
|
|
||||||
|
|
||||||
import (
|
|
||||||
`r00t2.io/goutils/bitmask`
|
|
||||||
)
|
|
||||||
|
|
||||||
// BitMask returns the underlying bitmask.MaskBit representation of a cmdArgOpt.
|
|
||||||
func (c cmdArgOpt) BitMask() (b bitmask.MaskBit) {
|
|
||||||
|
|
||||||
b = bitmask.MaskBit(c)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package exec_extra
|
|
||||||
|
|
||||||
import (
|
|
||||||
`testing`
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
testStruct struct {
|
|
||||||
Foo string `cmdarg:"short=f,long=foo"`
|
|
||||||
Bar int `cmdarg:"short=b,long=bar,prefer_short"`
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGetCmdFromStruct(t *testing.T) {
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var out []string
|
|
||||||
var v *testStruct = &testStruct{
|
|
||||||
Foo: "foo",
|
|
||||||
Bar: 123,
|
|
||||||
}
|
|
||||||
|
|
||||||
if out, err = GetCmdFromStruct(v); err != nil {
|
|
||||||
t.Fatalf("Received error getting command from struct: %v", err)
|
|
||||||
}
|
|
||||||
t.Logf("Got command args from struct:\n%#v", out)
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
package exec_extra
|
|
||||||
|
|
||||||
import (
|
|
||||||
`reflect`
|
|
||||||
|
|
||||||
`r00t2.io/goutils/bitmask`
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
cmdArgOpt bitmask.MaskBit
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
cmdArgFlag struct {
|
|
||||||
defaults *bitmask.MaskBit
|
|
||||||
fieldOpts *bitmask.MaskBit
|
|
||||||
boolMap map[string]bool
|
|
||||||
strMap map[string]string
|
|
||||||
shortFlag string
|
|
||||||
longFlag string
|
|
||||||
field *reflect.StructField
|
|
||||||
value *reflect.Value
|
|
||||||
argSlice *[]string
|
|
||||||
}
|
|
||||||
)
|
|
@ -1,3 +0,0 @@
|
|||||||
- XATTRS
|
|
||||||
(see FS_XFLAG_* in fs.h, FS_IOC_FSGETXATTR/FS_IOC_FSSETXATTR)
|
|
||||||
- fs label, UUID? (fs.h)
|
|
@ -1,36 +0,0 @@
|
|||||||
package fsutils
|
|
||||||
|
|
||||||
var (
|
|
||||||
/*
|
|
||||||
linuxFsAttrsListOrder defines the order the attributes are printed in per e2fsprogs.
|
|
||||||
|
|
||||||
See flags_name at https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git/tree/lib/e2p/pf.c for order.
|
|
||||||
Up to date as of e2fsprogs v1.47.1, Linux 6.12-rc7.
|
|
||||||
|
|
||||||
The below are the struct field names for easier reflection.
|
|
||||||
*/
|
|
||||||
linuxFsAttrsListOrder []string = []string{
|
|
||||||
"SecureDelete",
|
|
||||||
"UnDelete",
|
|
||||||
"SyncUpdate",
|
|
||||||
"DirSync",
|
|
||||||
"Immutable",
|
|
||||||
"AppendOnly",
|
|
||||||
"NoDumpFile",
|
|
||||||
"NoUpdateAtime",
|
|
||||||
"CompressFile",
|
|
||||||
"EncFile",
|
|
||||||
"ReservedExt3",
|
|
||||||
"HashIdxDir",
|
|
||||||
"NoMergeTail",
|
|
||||||
"DirTop",
|
|
||||||
"Extents",
|
|
||||||
"NoCOWFile",
|
|
||||||
"DAX",
|
|
||||||
"CaseInsensitive",
|
|
||||||
"ReservedExt4c",
|
|
||||||
"UseParentProjId",
|
|
||||||
"VerityProtected",
|
|
||||||
"NoCompress",
|
|
||||||
}
|
|
||||||
)
|
|
@ -1,127 +0,0 @@
|
|||||||
package fsutils
|
|
||||||
|
|
||||||
/*
|
|
||||||
https://github.com/torvalds/linux/blob/master/include/uapi/linux/fs.h "Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS)"
|
|
||||||
Up to date as of Linux 6.12-rc7.
|
|
||||||
*/
|
|
||||||
const (
|
|
||||||
SecureDelete fsAttr = 1 << iota // Secure deletion
|
|
||||||
UnDelete // Undelete
|
|
||||||
CompressFile // Compress file
|
|
||||||
SyncUpdate // Synchronous updates
|
|
||||||
Immutable // Immutable file
|
|
||||||
AppendOnly // Writes to file may only append
|
|
||||||
NoDumpFile // Do not dump file
|
|
||||||
NoUpdateAtime // Do not update atime
|
|
||||||
IsDirty // Nobody knows what this does, lol.
|
|
||||||
CompressedClusters // One or more compressed clusters
|
|
||||||
NoCompress // Don't compress
|
|
||||||
EncFile // Encrypted file
|
|
||||||
BtreeFmt // Btree format dir
|
|
||||||
AfsDir // AFS directory
|
|
||||||
ReservedExt3 // Reserved for ext3
|
|
||||||
NoMergeTail // File tail should not be merged
|
|
||||||
DirSync // dirsync behaviour (directories only)
|
|
||||||
DirTop // Top of directory hierarchies
|
|
||||||
ReservedExt4a // Reserved for ext4
|
|
||||||
Extents // Extents
|
|
||||||
VerityProtected // Verity-protected inode
|
|
||||||
LargeEaInode // Inode used for large EA
|
|
||||||
ReservedExt4b // Reserved for ext4
|
|
||||||
NoCOWFile // Do not cow file
|
|
||||||
_ // (Unused)
|
|
||||||
DAX // Inode is DAX
|
|
||||||
_ // (Unused)
|
|
||||||
_ // (Unused)
|
|
||||||
ReservedExt4c // Reserved for ext4
|
|
||||||
UseParentProjId // Create with parents projid
|
|
||||||
CaseInsensitive // Folder is case-insensitive
|
|
||||||
ReservedExt2 // Reserved for ext2 lib
|
|
||||||
)
|
|
||||||
|
|
||||||
// These are the same value. For some reason.
|
|
||||||
const (
|
|
||||||
HashIdxDir fsAttr = BtreeFmt // Hash-indexed directory
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// AttrNameValueMap contains a mapping of attribute names (as designated by this package) to their flag values.
|
|
||||||
AttrNameValueMap map[string]fsAttr = map[string]fsAttr{
|
|
||||||
"SecureDelete": SecureDelete,
|
|
||||||
"UnDelete": UnDelete,
|
|
||||||
"CompressFile": CompressFile,
|
|
||||||
"SyncUpdate": SyncUpdate,
|
|
||||||
"Immutable": Immutable,
|
|
||||||
"AppendOnly": AppendOnly,
|
|
||||||
"NoDumpFile": NoDumpFile,
|
|
||||||
"NoUpdateAtime": NoUpdateAtime,
|
|
||||||
"IsDirty": IsDirty,
|
|
||||||
"CompressedClusters": CompressedClusters,
|
|
||||||
"NoCompress": NoCompress,
|
|
||||||
"EncFile": EncFile,
|
|
||||||
"BtreeFmt": BtreeFmt,
|
|
||||||
"HashIdxDir": HashIdxDir,
|
|
||||||
"AfsDir": AfsDir,
|
|
||||||
"ReservedExt3": ReservedExt3,
|
|
||||||
"NoMergeTail": NoMergeTail,
|
|
||||||
"DirSync": DirSync,
|
|
||||||
"DirTop": DirTop,
|
|
||||||
"ReservedExt4a": ReservedExt4a,
|
|
||||||
"Extents": Extents,
|
|
||||||
"VerityProtected": VerityProtected,
|
|
||||||
"LargeEaInode": LargeEaInode,
|
|
||||||
"ReservedExt4b": ReservedExt4b,
|
|
||||||
"NoCOWFile": NoCOWFile,
|
|
||||||
"DAX": DAX,
|
|
||||||
"ReservedExt4c": ReservedExt4c,
|
|
||||||
"UseParentProjId": UseParentProjId,
|
|
||||||
"CaseInsensitive": CaseInsensitive,
|
|
||||||
"ReservedExt2": ReservedExt2,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
AttrValueNameMap contains a mapping of attribute flags to their names (as designated by this package).
|
|
||||||
Note the oddball here, BtreeFmt and HashIdxDir are actually the same value, so their string value is unpredictable.
|
|
||||||
*/
|
|
||||||
AttrValueNameMap map[fsAttr]string = invertMap(AttrNameValueMap)
|
|
||||||
|
|
||||||
// KernelNameValueMap allows lookups using the symbol name as used in the Linux kernel source.
|
|
||||||
KernelNameValueMap map[string]fsAttr = map[string]fsAttr{
|
|
||||||
"FS_SECRM_FL": SecureDelete,
|
|
||||||
"FS_UNRM_FL": UnDelete,
|
|
||||||
"FS_COMPR_FL": CompressFile,
|
|
||||||
"FS_SYNC_FL": SyncUpdate,
|
|
||||||
"FS_IMMUTABLE_FL": Immutable,
|
|
||||||
"FS_APPEND_FL": AppendOnly,
|
|
||||||
"FS_NODUMP_FL": NoDumpFile,
|
|
||||||
"FS_NOATIME_FL": NoUpdateAtime,
|
|
||||||
"FS_DIRTY_FL": IsDirty,
|
|
||||||
"FS_COMPRBLK_FL": CompressedClusters,
|
|
||||||
"FS_NOCOMP_FL": NoCompress,
|
|
||||||
"FS_ENCRYPT_FL": EncFile,
|
|
||||||
"FS_BTREE_FL": BtreeFmt,
|
|
||||||
"FS_INDEX_FL": HashIdxDir,
|
|
||||||
"FS_IMAGIC_FL": AfsDir,
|
|
||||||
"FS_JOURNAL_DATA_FL": ReservedExt3,
|
|
||||||
"FS_NOTAIL_FL": NoMergeTail,
|
|
||||||
"FS_DIRSYNC_FL": DirSync,
|
|
||||||
"FS_TOPDIR_FL": DirTop,
|
|
||||||
"FS_HUGE_FILE_FL": ReservedExt4a,
|
|
||||||
"FS_EXTENT_FL": Extents,
|
|
||||||
"FS_VERITY_FL": VerityProtected,
|
|
||||||
"FS_EA_INODE_FL": LargeEaInode,
|
|
||||||
"FS_EOFBLOCKS_FL": ReservedExt4b,
|
|
||||||
"FS_NOCOW_FL": NoCOWFile,
|
|
||||||
"FS_DAX_FL": DAX,
|
|
||||||
"FS_INLINE_DATA_FL": ReservedExt4c,
|
|
||||||
"FS_PROJINHERIT_FL": UseParentProjId,
|
|
||||||
"FS_CASEFOLD_FL": CaseInsensitive,
|
|
||||||
"FS_RESERVED_FL": ReservedExt2,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
KernelValueNameMap contains a mapping of attribute flags to their kernel source symbol name.
|
|
||||||
Note the oddball here, BtreeFmt and HashIdxDir are actually the same value, so their string value is unpredictable.
|
|
||||||
*/
|
|
||||||
KernelValueNameMap map[fsAttr]string = invertMap(KernelNameValueMap)
|
|
||||||
)
|
|
@ -1,7 +0,0 @@
|
|||||||
/*
|
|
||||||
fsutils is a collection of filesystem-related functions, types, etc.
|
|
||||||
|
|
||||||
Currently it's only a (fixed/actually working) reimplementation of github.com/g0rbe/go-chattr.
|
|
||||||
(Note to library maintainers, if someone reports an integer overflow and even tells you how to fix it, you should probably fix it.)
|
|
||||||
*/
|
|
||||||
package fsutils
|
|
@ -1,11 +0,0 @@
|
|||||||
package fsutils
|
|
||||||
|
|
||||||
import (
|
|
||||||
`syscall`
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// Yes, I know. "Why ENOTTY?" I don't know, ask Linus.
|
|
||||||
// If you see "inappropriate ioctl for device", it's this'un.
|
|
||||||
ErrFsAttrsUnsupported error = syscall.ENOTTY
|
|
||||||
)
|
|
@ -1,16 +0,0 @@
|
|||||||
package fsutils
|
|
||||||
|
|
||||||
// invertMap returns some handy consts remapping for easier lookups.
|
|
||||||
func invertMap(origMap map[string]fsAttr) (newMap map[fsAttr]string) {
|
|
||||||
|
|
||||||
if origMap == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
newMap = make(map[fsAttr]string)
|
|
||||||
|
|
||||||
for k, v := range origMap {
|
|
||||||
newMap[v] = k
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
package fsutils
|
|
||||||
|
|
||||||
import (
|
|
||||||
`reflect`
|
|
||||||
`strings`
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
String returns a string representation (comparable to lsattr(1)) of an FsAttrs.
|
|
||||||
|
|
||||||
Not all flags are represented, as this aims for compatibility with e2fsprogs/lsattr output.
|
|
||||||
*/
|
|
||||||
func (f *FsAttrs) String() (s string) {
|
|
||||||
|
|
||||||
// Flags have their short name printed if set, otherwise a '-' placeholder is used.
|
|
||||||
// https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git/tree/lib/e2p/pf.c
|
|
||||||
|
|
||||||
var refType reflect.Type
|
|
||||||
var refVal reflect.Value
|
|
||||||
var refField reflect.StructField
|
|
||||||
var fieldVal reflect.Value
|
|
||||||
var tagVal string
|
|
||||||
var sb strings.Builder
|
|
||||||
|
|
||||||
if f == nil {
|
|
||||||
s = strings.Repeat("-", len(linuxFsAttrsListOrder))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
refVal = reflect.ValueOf(*f)
|
|
||||||
refType = refVal.Type()
|
|
||||||
for _, fn := range linuxFsAttrsListOrder {
|
|
||||||
refField, _ = refType.FieldByName(fn)
|
|
||||||
tagVal = refField.Tag.Get("fsAttrShort")
|
|
||||||
if tagVal == "" || tagVal == "-" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fieldVal = refVal.FieldByName(fn)
|
|
||||||
if fieldVal.Bool() {
|
|
||||||
sb.WriteString(tagVal)
|
|
||||||
} else {
|
|
||||||
sb.WriteString("-")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s = sb.String()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
StringLong returns a more extensive/"human-friendly" representation (comparable to lsattr(1) wiih -l) of an Fsattrs.
|
|
||||||
|
|
||||||
Not all flags are represented, as this aims for compatibility with e2fsprogs/lsattr output.
|
|
||||||
*/
|
|
||||||
func (f *FsAttrs) StringLong() (s string) {
|
|
||||||
|
|
||||||
// The long names are separated via a commma then a space.
|
|
||||||
// If no attrs are set, the string "---" is used.
|
|
||||||
// https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git/tree/lib/e2p/pf.c
|
|
||||||
|
|
||||||
var refType reflect.Type
|
|
||||||
var refVal reflect.Value
|
|
||||||
var refField reflect.StructField
|
|
||||||
var fieldVal reflect.Value
|
|
||||||
var tagVal string
|
|
||||||
var out []string
|
|
||||||
|
|
||||||
if f == nil {
|
|
||||||
s = strings.Repeat("-", 3)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
refVal = reflect.ValueOf(*f)
|
|
||||||
refType = refVal.Type()
|
|
||||||
for _, fn := range linuxFsAttrsListOrder {
|
|
||||||
refField, _ = refType.FieldByName(fn)
|
|
||||||
tagVal = refField.Tag.Get("fsAttrLong")
|
|
||||||
if tagVal == "" || tagVal == "-" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fieldVal = refVal.FieldByName(fn)
|
|
||||||
if fieldVal.Bool() {
|
|
||||||
out = append(out, tagVal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if out == nil || len(out) == 0 {
|
|
||||||
s = strings.Repeat("-", 3)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s = strings.Join(out, ", ")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package fsutils
|
|
||||||
|
|
||||||
import (
|
|
||||||
`os`
|
|
||||||
`reflect`
|
|
||||||
|
|
||||||
`r00t2.io/sysutils/paths`
|
|
||||||
)
|
|
||||||
|
|
||||||
func (f *FsAttrs) Apply(path string) (err error) {
|
|
||||||
|
|
||||||
var file *os.File
|
|
||||||
var reflectVal reflect.Value
|
|
||||||
var fieldVal reflect.Value
|
|
||||||
|
|
||||||
if f == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = paths.RealPath(&path); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if file, err = os.Open(path); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
reflectVal = reflect.ValueOf(*f)
|
|
||||||
|
|
||||||
for attrNm, attrVal := range AttrNameValueMap {
|
|
||||||
fieldVal = reflectVal.FieldByName(attrNm)
|
|
||||||
if fieldVal.Bool() {
|
|
||||||
if err = setAttrs(file, attrVal); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err = unsetAttrs(file, attrVal); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,134 +0,0 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package fsutils
|
|
||||||
|
|
||||||
import (
|
|
||||||
`os`
|
|
||||||
`reflect`
|
|
||||||
`unsafe`
|
|
||||||
|
|
||||||
`golang.org/x/sys/unix`
|
|
||||||
`r00t2.io/goutils/bitmask`
|
|
||||||
`r00t2.io/sysutils/paths`
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetAttrs(path string) (attrs *FsAttrs, err error) {
|
|
||||||
|
|
||||||
var f *os.File
|
|
||||||
var evalAttrs FsAttrs
|
|
||||||
var attrVal fsAttr
|
|
||||||
var attrValBit bitmask.MaskBit
|
|
||||||
var reflectVal reflect.Value
|
|
||||||
var field reflect.Value
|
|
||||||
var myPath string = path
|
|
||||||
|
|
||||||
if err = paths.RealPath(&myPath); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if f, err = os.Open(myPath); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
reflectVal = reflect.ValueOf(&evalAttrs).Elem()
|
|
||||||
|
|
||||||
if attrVal, err = getAttrs(f); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
attrValBit = bitmask.MaskBit(attrVal)
|
|
||||||
|
|
||||||
for attrNm, attrInt := range AttrNameValueMap {
|
|
||||||
field = reflectVal.FieldByName(attrNm)
|
|
||||||
field.SetBool(attrValBit.HasFlag(bitmask.MaskBit(attrInt)))
|
|
||||||
}
|
|
||||||
|
|
||||||
attrs = new(FsAttrs)
|
|
||||||
*attrs = evalAttrs
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// getAttrs is the unexported low-level syscall to get attributes.
|
|
||||||
func getAttrs(f *os.File) (attrVal fsAttr, err error) {
|
|
||||||
|
|
||||||
var u uint
|
|
||||||
var curFlags int
|
|
||||||
// var errNo syscall.Errno
|
|
||||||
|
|
||||||
/*
|
|
||||||
if _, _, errNo = unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.FS_IOC_GETFLAGS, uintptr(unsafe.Pointer(&curFlags))); errNo != 0 {
|
|
||||||
err = os.NewSyscallError("ioctl: FS_IOC_GETFLAGS", errNo)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if curFlags, err = unix.IoctlGetInt(int(f.Fd()), unix.FS_IOC_GETFLAGS); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
u = uint(curFlags)
|
|
||||||
|
|
||||||
attrVal = fsAttr(u)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// setAttrs is the unexported low-level syscall to set attributes. attrs may be OR'd.
|
|
||||||
func setAttrs(f *os.File, attrs fsAttr) (err error) {
|
|
||||||
|
|
||||||
var curAttrs fsAttr
|
|
||||||
var ab bitmask.MaskBit
|
|
||||||
var errNo unix.Errno
|
|
||||||
var val uint
|
|
||||||
|
|
||||||
if curAttrs, err = getAttrs(f); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ab = bitmask.MaskBit(curAttrs)
|
|
||||||
|
|
||||||
if ab.HasFlag(bitmask.MaskBit(attrs)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ab.AddFlag(bitmask.MaskBit(attrs))
|
|
||||||
|
|
||||||
val = ab.Value()
|
|
||||||
|
|
||||||
/*
|
|
||||||
if err = unix.IoctlSetInt(int(f.Fd()), unix.FS_IOC_SETFLAGS, int(ab.Value())); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if _, _, errNo = unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.FS_IOC_SETFLAGS, uintptr(unsafe.Pointer(&val))); errNo != 0 {
|
|
||||||
err = os.NewSyscallError("ioctl: SYS_IOCTL", errNo)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// unsetAttrs is the unexported low-level syscall to remove attributes. attrs may be OR'd.
|
|
||||||
func unsetAttrs(f *os.File, attrs fsAttr) (err error) {
|
|
||||||
|
|
||||||
var curAttrs fsAttr
|
|
||||||
var ab bitmask.MaskBit
|
|
||||||
|
|
||||||
if curAttrs, err = getAttrs(f); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ab = bitmask.MaskBit(curAttrs)
|
|
||||||
|
|
||||||
if !ab.HasFlag(bitmask.MaskBit(attrs)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ab.ClearFlag(bitmask.MaskBit(attrs))
|
|
||||||
|
|
||||||
/*
|
|
||||||
if err = unix.IoctlSetInt(int(f.Fd()), unix.FS_IOC_SETFLAGS, int(ab.Value())); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package fsutils
|
|
||||||
|
|
||||||
import (
|
|
||||||
`errors`
|
|
||||||
`fmt`
|
|
||||||
`os`
|
|
||||||
`os/user`
|
|
||||||
`testing`
|
|
||||||
|
|
||||||
`github.com/davecgh/go-spew/spew`
|
|
||||||
`r00t2.io/sysutils/paths`
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
testFilename string = "testfile"
|
|
||||||
testErrBadUser error = errors.New("test must be run as root")
|
|
||||||
)
|
|
||||||
|
|
||||||
func testChkUser() (err error) {
|
|
||||||
var u *user.User
|
|
||||||
|
|
||||||
if u, err = user.Current(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if u.Uid != "0" {
|
|
||||||
err = testErrBadUser
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetAttrs(t *testing.T) {
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var attrs *FsAttrs
|
|
||||||
|
|
||||||
if attrs, err = GetAttrs(testFilename); err != nil {
|
|
||||||
t.Fatalf("Failed to get attrs for %v: %v", testFilename, err)
|
|
||||||
}
|
|
||||||
t.Logf("Attrs for %v (before):\n%s", testFilename, spew.Sdump(attrs))
|
|
||||||
attrs.CompressFile = true
|
|
||||||
attrs.SyncUpdate = true
|
|
||||||
attrs.SecureDelete = true
|
|
||||||
if err = attrs.Apply(testFilename); err != nil {
|
|
||||||
t.Fatalf("Failed to apply attrs to %v: %v", testFilename, err)
|
|
||||||
}
|
|
||||||
t.Logf("Applied new attrs to %v:\n%#v", testFilename, attrs)
|
|
||||||
if attrs, err = GetAttrs(testFilename); err != nil {
|
|
||||||
t.Fatalf("Failed to get attrs for %v: %v", testFilename, err)
|
|
||||||
}
|
|
||||||
t.Logf("Attrs for %v (after):\n%s", testFilename, spew.Sdump(attrs))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMain(t *testing.M) {
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var rslt int
|
|
||||||
|
|
||||||
if err = testChkUser(); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
if err = paths.RealPath(&testFilename); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
if err = os.WriteFile(testFilename, []byte("This is a test file."), 0o0644); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(3)
|
|
||||||
}
|
|
||||||
|
|
||||||
rslt = t.Run()
|
|
||||||
|
|
||||||
if err = os.Remove(testFilename); err != nil {
|
|
||||||
fmt.Printf("Failed to remove test file %v: %v", testFilename, err)
|
|
||||||
}
|
|
||||||
os.Exit(rslt)
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
package fsutils
|
|
||||||
|
|
||||||
import (
|
|
||||||
`r00t2.io/goutils/bitmask`
|
|
||||||
)
|
|
||||||
|
|
||||||
type fsAttr bitmask.MaskBit
|
|
||||||
|
|
||||||
/*
|
|
||||||
FsAttrs is a struct representation of filesystem attributes on Linux.
|
|
||||||
Up to date as of Linux 6.12-rc7.
|
|
||||||
*/
|
|
||||||
type FsAttrs struct {
|
|
||||||
SecureDelete bool `fsAttrShort:"s" fsAttrLong:"Secure_Deletion" fsAttrKern:"FS_SECRM_FL" json:"secure_delete" toml:"SecureDelete" yaml:"Secure Delete" xml:"secureDelete,attr"`
|
|
||||||
UnDelete bool `fsAttrShort:"u" fsAttrLong:"Undelete" fsAttrKern:"FS_UNRM_FL" json:"undelete" toml:"Undelete" yaml:"Undelete" xml:"undelete,attr"`
|
|
||||||
CompressFile bool `fsAttrShort:"c" fsAttrLong:"Compression_Requested" fsAttrKern:"FS_COMPR_FL" json:"compress" toml:"Compress" yaml:"Compress" xml:"compress,attr"`
|
|
||||||
SyncUpdate bool `fsAttrShort:"S" fsAttrLong:"Synchronous_Updates" fsAttrKern:"FS_SYNC_FL" json:"sync" toml:"SyncUpdate" yaml:"Synchronized Update" xml:"syncUpdate,attr"`
|
|
||||||
Immutable bool `fsAttrShort:"i" fsAttrLong:"Immutable" fsAttrKern:"FS_IMMUTABLE_FL" json:"immutable" toml:"Immutable" yaml:"Immutable" xml:"immutable,attr"`
|
|
||||||
AppendOnly bool `fsAttrShort:"a" fsAttrLong:"Append_Only" fsAttrKern:"FS_APPEND_FL" json:"append_only" toml:"AppendOnly" yaml:"Append Only" xml:"appendOnly,attr"`
|
|
||||||
NoDumpFile bool `fsAttrShort:"d" fsAttrLong:"No_Dump" fsAttrKern:"FS_NODUMP_FL" json:"no_dump" toml:"NoDump" yaml:"Disable Dumping" xml:"noDump,attr"`
|
|
||||||
NoUpdateAtime bool `fsAttrShort:"A" fsAttrLong:"No_Atime" fsAttrKern:"FS_NOATIME_FL" json:"no_atime" toml:"DisableAtime" yaml:"Disable Atime Updating" xml:"noAtime,attr"`
|
|
||||||
IsDirty bool `fsAttrShort:"-" fsAttrLong:"-" fsAttrKern:"FS_DIRTY_FL" json:"dirty" toml:"Dirty" yaml:"Dirty" xml:"dirty,attr"`
|
|
||||||
CompressedClusters bool `fsAttrShort:"-" fsAttrLong:"-" fsAttrKern:"FS_COMPRBLK_FL" json:"compress_clst" toml:"CompressedClusters" yaml:"Compressed Clusters" xml:"compressClst,attr"`
|
|
||||||
NoCompress bool `fsAttrShort:"m" fsAttrLong:"Dont_Compress" fsAttrKern:"FS_NOCOMP_FL" json:"no_compress" toml:"DisableCompression" yaml:"Disable Compression" xml:"noCompress,attr"`
|
|
||||||
EncFile bool `fsAttrShort:"E" fsAttrLong:"Encrypted" fsAttrKern:"FS_ENCRYPT_FL" json:"enc" toml:"Encrypted" yaml:"Encrypted" xml:"enc,attr"`
|
|
||||||
BtreeFmt bool `fsAttrShort:"-" fsAttrLong:"-" fsAttrKern:"FS_BTREE_FL" json:"btree" toml:"Btree" yaml:"Btree" xml:"btree,attr"`
|
|
||||||
HashIdxDir bool `fsAttrShort:"I" fsAttrLong:"Indexed_directory" fsAttrKern:"FS_INDEX_FL" json:"idx_dir" toml:"IdxDir" yaml:"Indexed Directory" xml:"idxDir,attr"`
|
|
||||||
AfsDir bool `fsAttrShort:"-" fsAttrLong:"-" fsAttrKern:"FS_IMAGIC_FL" json:"afs" toml:"AFS" yaml:"AFS" xml:"afs,attr"`
|
|
||||||
ReservedExt3 bool `fsAttrShort:"j" fsAttrLong:"Journaled_Data" fsAttrKern:"FS_JOURNAL_DATA_FL" json:"res_ext3" toml:"ReservedExt3" yaml:"Reserved Ext3" xml:"resExt3,attr"`
|
|
||||||
NoMergeTail bool `fsAttrShort:"t" fsAttrLong:"No_Tailmerging" fsAttrKern:"FS_NOTAIL_FL" json:"no_merge_tail" toml:"DisableTailmerging" yaml:"Disable Tailmerging" xml:"noMergeTail,attr"`
|
|
||||||
DirSync bool `fsAttrShort:"D" fsAttrLong:"Synchronous_Directory_Updates" fsAttrKern:"FS_DIRSYNC_FL" json:"dir_sync" toml:"DirSync" yaml:"Synchronized Directory Updates" xml:"dirSync,attr"`
|
|
||||||
DirTop bool `fsAttrShort:"T" fsAttrLong:"Top_of_Directory_Hierarchies" fsAttrKern:"FS_TOPDIR_FL" json:"dir_top" toml:"DirTop" yaml:"Top of Directory Hierarchies" xml:"dirTop,attr"`
|
|
||||||
ReservedExt4a bool `fsAttrShort:"-" fsAttrLong:"-" fsAttrKern:"FS_HUGE_FILE_FL" json:"res_ext4a" toml:"ReservedExt4A" yaml:"Reserved Ext4 A" xml:"resExt4a,attr"`
|
|
||||||
Extents bool `fsAttrShort:"e" fsAttrLong:"Extents" fsAttrKern:"FS_EXTENT_FL" json:"extents" toml:"Extents" yaml:"Extents" xml:"extents,attr"`
|
|
||||||
VerityProtected bool `fsAttrShort:"V" fsAttrLong:"Verity" fsAttrKern:"FS_VERITY_FL" json:"verity" toml:"Verity" yaml:"Verity Protected" xml:"verity,attr"`
|
|
||||||
LargeEaInode bool `fsAttrShort:"-" fsAttrLong:"-" fsAttrKern:"FS_EA_INODE_FL" json:"ea" toml:"EAInode" yaml:"EA Inode" xml:"ea,attr"`
|
|
||||||
ReservedExt4b bool `fsAttrShort:"-" fsAttrLong:"-" fsAttrKern:"FS_EOFBLOCKS_FL" json:"res_ext4b" toml:"ReservedExt4B" yaml:"Reserved Ext4 B" xml:"resExt4b,attr"`
|
|
||||||
NoCOWFile bool `fsAttrShort:"C" fsAttrLong:"No_COW" fsAttrKern:"FS_NOCOW_FL" json:"no_cow" toml:"NoCOW" yaml:"Disable COW" xml:"noCOW,attr"`
|
|
||||||
DAX bool `fsAttrShort:"x" fsAttrLong:"DAX" fsAttrKern:"FS_DAX_FL" json:"dax" toml:"DAX" yaml:"DAX" xml:"DAX,attr"`
|
|
||||||
ReservedExt4c bool `fsAttrShort:"N" fsAttrLong:"Inline_Data" fsAttrKern:"FS_INLINE_DATA_FL" json:"res_ext4c" toml:"ReservedExt4C" yaml:"Reserved Ext4 C" xml:"resExt4c,attr"`
|
|
||||||
UseParentProjId bool `fsAttrShort:"P" fsAttrLong:"Project_Hierarchy" fsAttrKern:"FS_PROJINHERIT_FL" json:"parent_proj_id" toml:"ParentProjId" yaml:"Use Parent Project ID" xml:"parentProjId,attr"`
|
|
||||||
CaseInsensitive bool `fsAttrShort:"F" fsAttrLong:"Casefold" fsAttrKern:"FS_CASEFOLD_FL" json:"case_ins" toml:"CaseInsensitive" yaml:"Case Insensitive" xml:"caseIns,attr"`
|
|
||||||
ReservedExt2 bool `fsAttrShort:"-" fsAttrLong:"-" fsAttrKern:"FS_RESERVED_FL" json:"res_ext2" toml:"ReservedExt2" yaml:"Reserved Ext2" xml:"resExt2,attr"`
|
|
||||||
}
|
|
163
funcs_idstate.go
163
funcs_idstate.go
@ -1,163 +0,0 @@
|
|||||||
package sysutils
|
|
||||||
|
|
||||||
// Checked consolidates all the provided checked functions.
|
|
||||||
func (i *IDState) Checked() (checked bool) {
|
|
||||||
|
|
||||||
if i == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
checked = i.uidsChecked &&
|
|
||||||
i.gidsChecked &&
|
|
||||||
i.sudoChecked &&
|
|
||||||
i.ppidUidChecked &&
|
|
||||||
i.ppidGidChecked
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
IsReal consolidates all the elevation/dropped-privs checks into a single method.
|
|
||||||
|
|
||||||
It will only return true if no sudo was detected and *all* UIDs/GIDs match.
|
|
||||||
*/
|
|
||||||
func (i *IDState) IsReal(real bool) {
|
|
||||||
|
|
||||||
if i == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
real = true
|
|
||||||
|
|
||||||
for _, b := range []bool{
|
|
||||||
i.IsSuid(),
|
|
||||||
i.IsSgid(),
|
|
||||||
i.IsSudoUser(),
|
|
||||||
i.IsSudoGroup(),
|
|
||||||
} {
|
|
||||||
if b {
|
|
||||||
real = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
IsSudoGroup is true if any of the group sudo env vars are set,
|
|
||||||
or the parent process has a different group (and is not PID 1).
|
|
||||||
|
|
||||||
It will always return false if SudoChecked returns false oor PPIDGIDsChecked returns false.
|
|
||||||
*/
|
|
||||||
func (i *IDState) IsSudoGroup() (sudo bool) {
|
|
||||||
|
|
||||||
if i == nil || !i.sudoChecked || !i.ppidGidChecked {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sudo = i.SudoEnvGroup || !i.PPIDGidMatch
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
IsSudoUser is true if any of the user sudo env vars are set,
|
|
||||||
or the parent process has a different owner (and is not PID 1).
|
|
||||||
|
|
||||||
It will always return false if SudoChecked returns false or PPIDUIDsChecked returns false.
|
|
||||||
*/
|
|
||||||
func (i *IDState) IsSudoUser() (sudo bool) {
|
|
||||||
|
|
||||||
if i == nil || !i.sudoChecked || !i.ppidUidChecked {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sudo = i.SudoEnvUser || !i.PPIDUidMatch
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSuid is true if the RUID does not match EUID or SUID. It will always return false if UIDsChecked returns false.
|
|
||||||
func (i *IDState) IsSuid() (suid bool) {
|
|
||||||
|
|
||||||
if i == nil || !i.uidsChecked {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
suid = i.RUID != i.EUID || i.RUID != i.SUID
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSgid is true if the RGID does not match EGID or SGID. It will always return false if GIDsChecked returns false.
|
|
||||||
func (i *IDState) IsSgid() (sgid bool) {
|
|
||||||
|
|
||||||
if i == nil || !i.gidsChecked {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sgid = i.RGID != i.EGID || i.RGID != i.SGID
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GIDsChecked is true if the GIDs presented can be trusted.
|
|
||||||
func (i *IDState) GIDsChecked() (checked bool) {
|
|
||||||
|
|
||||||
if i == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
checked = i.gidsChecked
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// PPIDGIDsChecked is true if PPIDGidMatch can be trusted.
|
|
||||||
func (i *IDState) PPIDGIDsChecked() (checked bool) {
|
|
||||||
|
|
||||||
if i == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
checked = i.ppidGidChecked
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// PPIDUIDsChecked is true if PPIDUidMatch can be trusted.
|
|
||||||
func (i *IDState) PPIDUIDsChecked() (checked bool) {
|
|
||||||
|
|
||||||
if i == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
checked = i.ppidUidChecked
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// SudoChecked is true if SudoEnvVars can be trusted
|
|
||||||
func (i *IDState) SudoChecked() (checked bool) {
|
|
||||||
|
|
||||||
if i == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
checked = i.sudoChecked
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// UIDsChecked is true if the UIDs presented can be trusted.
|
|
||||||
func (i *IDState) UIDsChecked() (checked bool) {
|
|
||||||
|
|
||||||
if i == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
checked = i.uidsChecked
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
package sysutils
|
|
||||||
|
|
||||||
import (
|
|
||||||
`fmt`
|
|
||||||
`os`
|
|
||||||
|
|
||||||
`golang.org/x/sys/unix`
|
|
||||||
`r00t2.io/sysutils/envs`
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetIDState returns current ID/elevation information. An IDState should *not* be explicitly created/defined.
|
|
||||||
func GetIDState() (ids IDState) {
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
ids.RUID, ids.EUID, ids.SUID = unix.Getresuid()
|
|
||||||
ids.uidsChecked = true
|
|
||||||
ids.RGID, ids.EGID, ids.SGID = unix.Getresgid()
|
|
||||||
ids.gidsChecked = true
|
|
||||||
|
|
||||||
ids.SudoEnvCmd = envs.HasEnv("SUDO_COMMAND")
|
|
||||||
ids.SudoEnvHome = envs.HasEnv("SUDO_HOME")
|
|
||||||
ids.SudoEnvGroup = envs.HasEnv("SUDO_GID")
|
|
||||||
ids.SudoEnvUser = envs.HasEnv("SUDO_UID") || envs.HasEnv("SUDO_USER")
|
|
||||||
if ids.SudoEnvCmd || ids.SudoEnvHome || ids.SudoEnvGroup || ids.SudoEnvUser {
|
|
||||||
ids.SudoEnvVars = true
|
|
||||||
}
|
|
||||||
ids.sudoChecked = true
|
|
||||||
|
|
||||||
// PID 1 will *always* be root, so that can return a false positive for sudo.
|
|
||||||
if os.Getppid() != 1 {
|
|
||||||
ids.stat = new(unix.Stat_t)
|
|
||||||
if err = unix.Stat(
|
|
||||||
fmt.Sprintf("/proc/%d/stat", os.Getppid()),
|
|
||||||
ids.stat,
|
|
||||||
); err != nil {
|
|
||||||
err = nil
|
|
||||||
} else {
|
|
||||||
ids.PPIDUidMatch = ids.RUID == int(ids.stat.Uid)
|
|
||||||
ids.ppidUidChecked = true
|
|
||||||
ids.PPIDGidMatch = ids.RGID == int(ids.stat.Gid)
|
|
||||||
ids.ppidGidChecked = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ids.ppidUidChecked = true
|
|
||||||
ids.ppidGidChecked = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
23
go.mod
23
go.mod
@ -1,24 +1,3 @@
|
|||||||
module r00t2.io/sysutils
|
module r00t2.io/sysutils
|
||||||
|
|
||||||
go 1.23.2
|
go 1.16
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/davecgh/go-spew v1.1.1
|
|
||||||
github.com/djherbis/times v1.6.0
|
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
|
||||||
github.com/shirou/gopsutil/v4 v4.25.5
|
|
||||||
golang.org/x/sync v0.15.0
|
|
||||||
golang.org/x/sys v0.33.0
|
|
||||||
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8
|
|
||||||
r00t2.io/goutils v1.8.1
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/ebitengine/purego v0.8.4 // indirect
|
|
||||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
|
||||||
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
|
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
|
||||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
|
||||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
|
||||||
)
|
|
||||||
|
47
go.sum
47
go.sum
@ -1,47 +0,0 @@
|
|||||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
|
|
||||||
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
|
|
||||||
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
|
|
||||||
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
|
||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
|
||||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
|
||||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc=
|
|
||||||
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
|
||||||
github.com/shirou/gopsutil/v4 v4.25.5 h1:rtd9piuSMGeU8g1RMXjZs9y9luK5BwtnG7dZaQUJAsc=
|
|
||||||
github.com/shirou/gopsutil/v4 v4.25.5/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
|
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
|
||||||
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
|
||||||
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
|
||||||
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
|
||||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
|
||||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
|
||||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
|
||||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
|
||||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8 h1:FW42yWB1sGClqswyHIB68wo0+oPrav1IuQ+Tdy8Qp8E=
|
|
||||||
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8/go.mod h1:44w9OfBSQ9l3o59rc2w3AnABtE44bmtNnRMNC7z+oKE=
|
|
||||||
r00t2.io/goutils v1.8.1 h1:TQcUycPKsYn0QI4uCqb56utmvu/vVSxlblBg98iXStg=
|
|
||||||
r00t2.io/goutils v1.8.1/go.mod h1:9ObJI9S71wDLTOahwoOPs19DhZVYrOh4LEHmQ8SW4Lk=
|
|
||||||
r00t2.io/sysutils v1.1.1/go.mod h1:Wlfi1rrJpoKBOjWiYM9rw2FaiZqraD6VpXyiHgoDo/o=
|
|
@ -1,14 +0,0 @@
|
|||||||
//go:build unix
|
|
||||||
|
|
||||||
package ispriv
|
|
||||||
|
|
||||||
const (
|
|
||||||
sudoEnvPfx string = "SUDO_"
|
|
||||||
sudoUidEnv string = sudoEnvPfx + "UID"
|
|
||||||
sudoGidEnv string = sudoEnvPfx + "GID"
|
|
||||||
sudoUnameEnv string = sudoEnvPfx + "USER"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
curLoginUidFile string = "/proc/self/loginuid"
|
|
||||||
)
|
|
@ -1,7 +0,0 @@
|
|||||||
//go:build unix
|
|
||||||
|
|
||||||
/*
|
|
||||||
ispriv provides functions and a method to determine if a process is being run SUID/SGID, under sudo, etc.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package ispriv
|
|
@ -1,7 +0,0 @@
|
|||||||
//go:build windows
|
|
||||||
|
|
||||||
/*
|
|
||||||
ispriv provides functions on Windows to determine the currentl privilege status.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package ispriv
|
|
@ -1,68 +0,0 @@
|
|||||||
//go:build unix
|
|
||||||
|
|
||||||
package ispriv
|
|
||||||
|
|
||||||
import (
|
|
||||||
`os`
|
|
||||||
|
|
||||||
`github.com/shirou/gopsutil/v4/process`
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
GetProcIDs returns a ProcIDs from a given PID. An error will be raised if the process ID doesn't exist.
|
|
||||||
A negative value indicates "self" (see also GetProcIDsSelf).
|
|
||||||
|
|
||||||
Note that if you are not EUID == 0 (root) or you/the sudo target user does not own the process,
|
|
||||||
the returning ProcIDs is HIGHLY LIKELY to be very inaccurate.
|
|
||||||
*/
|
|
||||||
func GetProcIDs(pid int32) (p *ProcIDs, err error) {
|
|
||||||
|
|
||||||
var proc ProcIDs
|
|
||||||
var ids []uint32
|
|
||||||
|
|
||||||
if pid < 0 {
|
|
||||||
pid = int32(os.Getpid())
|
|
||||||
}
|
|
||||||
|
|
||||||
if proc.proc, err = process.NewProcess(pid); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ids, err = proc.proc.Gids(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.gids = &IdInfo{
|
|
||||||
real: uint(ids[0]),
|
|
||||||
effective: uint(ids[1]),
|
|
||||||
savedSet: uint(ids[2]),
|
|
||||||
filesystem: nil,
|
|
||||||
}
|
|
||||||
if len(ids) == 4 {
|
|
||||||
p.gids.filesystem = new(uint)
|
|
||||||
*p.gids.filesystem = uint(ids[3])
|
|
||||||
}
|
|
||||||
if ids, err = proc.proc.Uids(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.uids = &IdInfo{
|
|
||||||
real: uint(ids[0]),
|
|
||||||
effective: uint(ids[1]),
|
|
||||||
savedSet: uint(ids[2]),
|
|
||||||
filesystem: nil,
|
|
||||||
}
|
|
||||||
if len(ids) == 4 {
|
|
||||||
p.uids.filesystem = new(uint)
|
|
||||||
*p.uids.filesystem = uint(ids[3])
|
|
||||||
}
|
|
||||||
|
|
||||||
p = &proc
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetProcIDsSelf returns a ProcIDs from the current process' PID.
|
|
||||||
func GetProcIDsSelf() (p *ProcIDs, err error) {
|
|
||||||
|
|
||||||
p, err = GetProcIDs(int32(os.Getpid()))
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,426 +0,0 @@
|
|||||||
//go:build unix
|
|
||||||
|
|
||||||
package ispriv
|
|
||||||
|
|
||||||
import (
|
|
||||||
`errors`
|
|
||||||
`os`
|
|
||||||
`os/user`
|
|
||||||
`strconv`
|
|
||||||
`strings`
|
|
||||||
|
|
||||||
`github.com/shirou/gopsutil/v4/process`
|
|
||||||
`golang.org/x/sys/unix`
|
|
||||||
`r00t2.io/sysutils/envs`
|
|
||||||
`r00t2.io/sysutils/paths`
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetEffective returns the EUID/EGID.
|
|
||||||
func (p *ProcIDs) GetEffective() (euid, egid uint) {
|
|
||||||
|
|
||||||
euid = p.uids.effective
|
|
||||||
egid = p.gids.effective
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFS returns the FSUID/FSGID. Not all platforms have this, in which case they'll be nil.
|
|
||||||
func (p *ProcIDs) GetFS() (fsuid, fsgid *uint) {
|
|
||||||
|
|
||||||
if p.uids.filesystem != nil {
|
|
||||||
fsuid = new(uint)
|
|
||||||
*fsuid = *p.uids.filesystem
|
|
||||||
}
|
|
||||||
if p.gids.filesystem != nil {
|
|
||||||
fsgid = new(uint)
|
|
||||||
*fsgid = *p.gids.filesystem
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
GetGids returms a set of a ProcIDs GIDs.
|
|
||||||
fs will be nil if unsupported on the platform.
|
|
||||||
If invoked with SGID, "savedSet" will be the SGID GID.
|
|
||||||
*/
|
|
||||||
func (p *ProcIDs) GetGids() (real, effective, savedSet uint, fs *uint) {
|
|
||||||
|
|
||||||
real = p.gids.real
|
|
||||||
effective = p.gids.effective
|
|
||||||
savedSet = p.gids.savedSet
|
|
||||||
if p.gids.filesystem != nil {
|
|
||||||
fs = new(uint)
|
|
||||||
*fs = *p.gids.filesystem
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetReal returns the (R)UID/(R)GID.
|
|
||||||
func (p *ProcIDs) GetReal() (ruid, rgid uint) {
|
|
||||||
|
|
||||||
ruid = p.uids.real
|
|
||||||
rgid = p.gids.real
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSaved returns the SUID/SGID.
|
|
||||||
func (p *ProcIDs) GetSaved() (suid, sgid uint) {
|
|
||||||
|
|
||||||
suid = p.uids.savedSet
|
|
||||||
sgid = p.gids.savedSet
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
GetUids returms a set of a ProcIDs UIDs.
|
|
||||||
fs will be nil if unsupported on the platform.
|
|
||||||
If invoked with SUID, "savedSet" will be the SUID UID.
|
|
||||||
*/
|
|
||||||
func (p *ProcIDs) GetUids() (real, effective, savedSet uint, fs *uint) {
|
|
||||||
|
|
||||||
real = p.uids.real
|
|
||||||
effective = p.uids.effective
|
|
||||||
savedSet = p.uids.savedSet
|
|
||||||
if p.uids.filesystem != nil {
|
|
||||||
fs = new(uint)
|
|
||||||
*fs = *p.uids.filesystem
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
IsSGID returns true if the process is Set GID/SGID.
|
|
||||||
|
|
||||||
Note that it will return false if invoked by a group with the same GID as an SGID that's set.
|
|
||||||
*/
|
|
||||||
func (p *ProcIDs) IsSGID() (isSgid bool) {
|
|
||||||
|
|
||||||
isSgid = p.gids.real != p.gids.savedSet
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
IsSUID returns true if the process is Set UID/SUID.
|
|
||||||
|
|
||||||
Note that it will return false if invoked by a user with the same UID as an SUID that's set.
|
|
||||||
*/
|
|
||||||
func (p *ProcIDs) IsSUID() (isSuid bool) {
|
|
||||||
|
|
||||||
isSuid = p.uids.real != p.uids.savedSet
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
IsSudo does a very fast (and potentially inaccurate) evaluation of whether the process is running under sudo.
|
|
||||||
|
|
||||||
DO NOT use this function for security-sensitive uses, fully accurate results, or critical implementations!
|
|
||||||
Use IsSudoWithConfidence instead for those cases.
|
|
||||||
IsSudo only does the most basic of checking, which can be easily and completely overridden by a non-privileged user.
|
|
||||||
*/
|
|
||||||
func (p *ProcIDs) IsSudo() (isSudo bool) {
|
|
||||||
|
|
||||||
// This is how every other Joe Blow does this. It's an extremely dumb way to do it. The caller has been warned.
|
|
||||||
for k, _ := range envs.GetEnvMap() {
|
|
||||||
if strings.HasPrefix(k, sudoEnvPfx) {
|
|
||||||
isSudo = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
IsSudoDetailed returns true for a very fast evaluation of whether the process is running under sudo,
|
|
||||||
and information about that context.
|
|
||||||
(If isSudo is false, originalUid/originalGid will both be -1 and originalUser will be nil.)
|
|
||||||
|
|
||||||
DO NOT use this function for security-sensitive uses, fully accurate results, or critical implementations!
|
|
||||||
Use IsSudoWithConfidenceDetailed instead for those cases.
|
|
||||||
IsSudoDetailed only does the most basic of checking, which can be easily and completely overridden by a non-privileged user.
|
|
||||||
*/
|
|
||||||
func (p *ProcIDs) IsSudoDetailed() (isSudo bool, originalUid, originalGid int, originalUser *user.User, err error) {
|
|
||||||
|
|
||||||
if originalUid, originalGid, originalUser, err = p.getSudoInfoEnv(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if originalUid >= 0 || originalGid >= 0 || originalUser != nil {
|
|
||||||
isSudo = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
IsSudoWithConfidence is like IsSudo, but is *much* more throrough.
|
|
||||||
|
|
||||||
It not only returns isSudo, which is true if *any* indicators pass,
|
|
||||||
but also:
|
|
||||||
|
|
||||||
* a confidence value (which indicates *how many* indicators *passed*)
|
|
||||||
* a maxConfidence value (which indicates how many indicators were *tested*)
|
|
||||||
* a score value (which is a float indicating overall confidence on a fixed and weighted scale; higher is more confident, 1.0 indicates 100% confidence)
|
|
||||||
*/
|
|
||||||
func (p *ProcIDs) IsSudoWithConfidence() (isSudo bool, confidence, maxConfidence uint, score float64, err error) {
|
|
||||||
|
|
||||||
// confidence/maxConfidence are not used directly; they're unweighted counters.
|
|
||||||
var scoreConf uint
|
|
||||||
var scoreMaxConf uint
|
|
||||||
|
|
||||||
score = float64(scoreConf) / float64(scoreMaxConf)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
IsSudoWithConfidenceDetailed is like IsSudoDetailed, but is *much* more throrough.
|
|
||||||
|
|
||||||
It not only returns the same results as IsSudoDetailed, but includes the same scoring values/system as IsSudoWithConfidence.
|
|
||||||
*/
|
|
||||||
func (p *ProcIDs) IsSudoWithConfidenceDetailed() (isSudo bool, confidence, maxConfidence uint, score float64, originalUid, originalGid int, originalUser *user.User, err error) {
|
|
||||||
|
|
||||||
var b []byte
|
|
||||||
var ok bool
|
|
||||||
var permErr bool
|
|
||||||
var envUid int
|
|
||||||
var envGid int
|
|
||||||
var scoreConf uint
|
|
||||||
var scoreMaxConf uint
|
|
||||||
var curUser *user.User
|
|
||||||
var envUser *user.User
|
|
||||||
var curUid uint64
|
|
||||||
var fstat unix.Stat_t
|
|
||||||
var fsUid int
|
|
||||||
var procFiles []process.OpenFilesStat
|
|
||||||
var loginUidFile string = curLoginUidFile
|
|
||||||
|
|
||||||
if curUser, err = user.Current(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if curUid, err = strconv.ParseUint(curUser.Uid, 10, 32); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if procFiles, err = p.proc.OpenFiles(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Env vars; only score 1x/each.
|
|
||||||
maxConfidence += 3
|
|
||||||
scoreMaxConf += 3
|
|
||||||
if envUid, envGid, envUser, err = p.getSudoInfoEnv(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
originalUid, originalGid, originalUser = envUid, envGid, envUser
|
|
||||||
if envUid >= 0 {
|
|
||||||
confidence++
|
|
||||||
scoreConf++
|
|
||||||
}
|
|
||||||
if envGid >= 0 {
|
|
||||||
confidence++
|
|
||||||
scoreConf++
|
|
||||||
}
|
|
||||||
if envUser != nil {
|
|
||||||
confidence++
|
|
||||||
scoreConf++
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
TTY/PTY ownership. We (can) only check this if we're running in an interactive session.
|
|
||||||
|
|
||||||
Typically this is done via (golang.org/x/term).IsTerminal(),
|
|
||||||
That pulls in a bunch of stuff I don't need, though, so I'll just replicate (...).IsTerminal() here;
|
|
||||||
it's just a wrapped single function call.
|
|
||||||
*/
|
|
||||||
// procFiles[0] is always STDIN. Whether it's a pipe, or TTY/PTY, or file, etc.
|
|
||||||
// (likewise, procFiles[1] is always STDOUT, procFiles[2] is always STDERR); however...
|
|
||||||
if _, err = unix.IoctlGetTermios(int(procFiles[0].Fd), unix.TCGETS); err == nil {
|
|
||||||
// Interactive
|
|
||||||
maxConfidence++
|
|
||||||
// This is only worth 2. It's pretty hard to fake unless origin user is root,
|
|
||||||
// but it's ALSO usually set to the target user.
|
|
||||||
scoreMaxConf += 2
|
|
||||||
fstat = unix.Stat_t{}
|
|
||||||
if err = unix.Fstat(int(procFiles[0].Fd), &fstat); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if uint64(fstat.Uid) != curUid {
|
|
||||||
// This is a... *potential* indicator, if a lateral sudo was done (user1 => user2),
|
|
||||||
// or root used sudo to *drop* privs to a regular user.
|
|
||||||
// We mark it as a pass for confidence since it IS a terminal, and it's permission-related.
|
|
||||||
confidence++
|
|
||||||
scoreConf += 2
|
|
||||||
originalUid = int(fstat.Uid)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// err is OK; just means non-interactive. No counter or score/max score increase; basically a NO-OP.
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// /proc/self/loginuid
|
|
||||||
// This is a REALLY good indicator. Probably the strongest next to reverse-walking the proc tree. It depends on PAM and auditd support, I think,
|
|
||||||
// BUT if it's present it's *really* really strong.
|
|
||||||
if ok, err = paths.RealPathExists(&loginUidFile); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
maxConfidence++
|
|
||||||
scoreMaxConf += 5
|
|
||||||
if b, err = os.ReadFile(loginUidFile); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if fsUid, err = strconv.Atoi(strings.TrimSpace(string(b))); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if uint64(fsUid) != curUid {
|
|
||||||
confidence++
|
|
||||||
scoreConf += 5
|
|
||||||
originalUid = fsUid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// proc tree reverse walking.
|
|
||||||
// This is, by far, the most reliable method.
|
|
||||||
// There are some valid conditions in which this would fail due to permissions
|
|
||||||
// (e.g. lateral sudo: user1 => user2), but if it's a permission error it's *probably*
|
|
||||||
// a lateral move anyways.
|
|
||||||
if isSudo, permErr, originalUid, originalGid, originalUser, err = p.revProcWalk(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
maxConfidence++
|
|
||||||
scoreMaxConf += 10
|
|
||||||
if permErr {
|
|
||||||
confidence++
|
|
||||||
scoreConf += 5
|
|
||||||
} else if isSudo {
|
|
||||||
confidence++
|
|
||||||
scoreConf += 10
|
|
||||||
}
|
|
||||||
|
|
||||||
score = float64(scoreConf) / float64(scoreMaxConf)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
getSudoInfoEnv returns env var driven sudo information.
|
|
||||||
|
|
||||||
These are in no way guaranteed to be accurate as the user can remove or override them.
|
|
||||||
*/
|
|
||||||
func (p *ProcIDs) getSudoInfoEnv() (uid, gid int, u *user.User, err error) {
|
|
||||||
|
|
||||||
var ok bool
|
|
||||||
var val string
|
|
||||||
var envMap map[string]string = envs.GetEnvMap()
|
|
||||||
|
|
||||||
uid = -1
|
|
||||||
gid = -1
|
|
||||||
|
|
||||||
if val, ok = envMap[sudoUnameEnv]; ok {
|
|
||||||
if u, err = user.Lookup(val); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if val, ok = envMap[sudoUidEnv]; ok {
|
|
||||||
if uid, err = strconv.Atoi(val); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if val, ok = envMap[sudoGidEnv]; ok {
|
|
||||||
if gid, err = strconv.Atoi(val); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
revProcWalk walks up the process tree ("proctree") until it either:
|
|
||||||
|
|
||||||
* finds a process invoked with sudo (true)
|
|
||||||
* hits PID == 1 (false)
|
|
||||||
* hits a permission error (true-ish)
|
|
||||||
*/
|
|
||||||
func (p *ProcIDs) revProcWalk() (sudoFound, isPermErr bool, origUid, origGid int, origUser *user.User, err error) {
|
|
||||||
|
|
||||||
var cmd []string
|
|
||||||
var parent *ProcIDs
|
|
||||||
var parentPid int32
|
|
||||||
var parentUname string
|
|
||||||
var parentUids []uint32
|
|
||||||
var parentGids []uint32
|
|
||||||
|
|
||||||
origUid = -1
|
|
||||||
origGid = -1
|
|
||||||
|
|
||||||
parent = p
|
|
||||||
for {
|
|
||||||
if parent == nil || parent.proc.Pid == 1 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if cmd, err = parent.proc.CmdlineSlice(); err != nil {
|
|
||||||
if errors.Is(err, os.ErrPermission) {
|
|
||||||
isPermErr = true
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if cmd[0] == "sudo" {
|
|
||||||
sudoFound = true
|
|
||||||
if parentUname, err = parent.proc.Username(); err != nil {
|
|
||||||
if errors.Is(err, os.ErrPermission) {
|
|
||||||
isPermErr = true
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if parentUids, err = parent.proc.Uids(); err != nil {
|
|
||||||
if errors.Is(err, os.ErrPermission) {
|
|
||||||
isPermErr = true
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if parentGids, err = parent.proc.Gids(); err != nil {
|
|
||||||
if errors.Is(err, os.ErrPermission) {
|
|
||||||
isPermErr = true
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if origUser, err = user.Lookup(parentUname); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
origUid = int(parentUids[0])
|
|
||||||
origGid = int(parentGids[0])
|
|
||||||
}
|
|
||||||
if sudoFound {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if parentPid, err = parent.proc.Ppid(); err != nil {
|
|
||||||
if errors.Is(err, os.ErrPermission) {
|
|
||||||
isPermErr = true
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if parent, err = GetProcIDs(parentPid); err != nil {
|
|
||||||
if errors.Is(err, os.ErrPermission) {
|
|
||||||
isPermErr = true
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
//go:build windows
|
|
||||||
|
|
||||||
package ispriv
|
|
||||||
|
|
||||||
import (
|
|
||||||
`golang.org/x/sys/windows`
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsAdmin returns true if currently running with Administrator privileges.
|
|
||||||
func IsAdmin() (admin bool, err error) {
|
|
||||||
|
|
||||||
var sid *windows.SID
|
|
||||||
var tok windows.Token
|
|
||||||
|
|
||||||
if err = windows.AllocateAndInitializeSid(
|
|
||||||
&windows.SECURITY_NT_AUTHORITY, // identAuth
|
|
||||||
2, // subAuth
|
|
||||||
windows.SECURITY_BUILTIN_DOMAIN_RID, // subAuth0
|
|
||||||
windows.DOMAIN_ALIAS_RID_ADMINS, // subAuth1
|
|
||||||
0, 0, 0, 0, 0, 0, // subAuth2-10
|
|
||||||
&sid, // sid
|
|
||||||
); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer windows.FreeSid(sid)
|
|
||||||
|
|
||||||
tok = windows.Token(0)
|
|
||||||
if admin, err = tok.IsMember(sid); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsElevated returns true if running in an elevated ("Run as Administrator") context.
|
|
||||||
func IsElevated() (elevated bool) {
|
|
||||||
|
|
||||||
var tok windows.Token = windows.Token(0)
|
|
||||||
|
|
||||||
elevated = tok.IsElevated()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
IsPrivileged indicates that the current security context is running both
|
|
||||||
with Administrator priviliges AND is elevated.
|
|
||||||
*/
|
|
||||||
func IsPrivileged() (privileged bool, err error) {
|
|
||||||
|
|
||||||
if privileged, err = IsAdmin(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if privileged {
|
|
||||||
privileged = IsElevated()
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
//go:build unix
|
|
||||||
|
|
||||||
package ispriv
|
|
||||||
|
|
||||||
import (
|
|
||||||
`github.com/shirou/gopsutil/v4/process`
|
|
||||||
)
|
|
||||||
|
|
||||||
type ProcIDs struct {
|
|
||||||
proc *process.Process
|
|
||||||
uids *IdInfo
|
|
||||||
gids *IdInfo
|
|
||||||
}
|
|
||||||
type IdInfo struct {
|
|
||||||
real uint
|
|
||||||
effective uint
|
|
||||||
savedSet uint
|
|
||||||
filesystem *uint
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
- search criteria should *also* support a timestamp range (e.g. so a search can be restricted to both older than AND newer than; e.g. older than 00:00, newer than 01:00)
|
|
@ -1,42 +0,0 @@
|
|||||||
package paths
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/fs"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Mostly just for reference.
|
|
||||||
const (
|
|
||||||
// ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeCharDevice | ModeIrregular
|
|
||||||
modeDir pathMode = pathMode(fs.ModeDir)
|
|
||||||
modeSymlink pathMode = pathMode(fs.ModeSymlink)
|
|
||||||
modePipe pathMode = pathMode(fs.ModeNamedPipe)
|
|
||||||
modeSocket pathMode = pathMode(fs.ModeSocket)
|
|
||||||
modeDev pathMode = pathMode(fs.ModeDevice)
|
|
||||||
modeCharDev pathMode = pathMode(fs.ModeCharDevice)
|
|
||||||
modeIrregular pathMode = pathMode(fs.ModeIrregular)
|
|
||||||
modeAnyExceptRegular pathMode = modeDir | modeSymlink | modePipe | modeSocket | modeDev | modeCharDev | modeIrregular
|
|
||||||
)
|
|
||||||
|
|
||||||
// Miss reasons
|
|
||||||
const (
|
|
||||||
MissNoMiss missReason = ""
|
|
||||||
MissNoMeta missReason = "Could not determine metadata"
|
|
||||||
MissBadBase missReason = "Base name does not match BasePtrn"
|
|
||||||
MissBadPath missReason = "Path does not match PathPtrn"
|
|
||||||
MissBadTime missReason = "Time(s) does not/do not match Age"
|
|
||||||
MissFile missReason = "Object is a file and NoFiles is set"
|
|
||||||
MissType missReason = "Object does not match TargetType"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Times
|
|
||||||
const TimeAny pathTimeType = 0
|
|
||||||
const (
|
|
||||||
// TimeAccessed == atime
|
|
||||||
TimeAccessed pathTimeType = 1 << iota
|
|
||||||
// TimeCreated == "birth" time (*NOT* ctime! See TimeChanged)
|
|
||||||
TimeCreated
|
|
||||||
// TimeChanged == ctime
|
|
||||||
TimeChanged
|
|
||||||
// TimeModified == mtime
|
|
||||||
TimeModified
|
|
||||||
)
|
|
@ -1,12 +0,0 @@
|
|||||||
package paths
|
|
||||||
|
|
||||||
import (
|
|
||||||
`errors`
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrNilErrChan error = errors.New("an initialized error channel is required")
|
|
||||||
ErrNilMatchChan error = errors.New("an initialized matches channel is required")
|
|
||||||
ErrNilMismatchChan error = errors.New("an initialized mismatches channel is required")
|
|
||||||
ErrNilWg error = errors.New("a non-nil sync.WaitGroup is required")
|
|
||||||
)
|
|
319
paths/funcs.go
319
paths/funcs.go
@ -19,22 +19,15 @@
|
|||||||
package paths
|
package paths
|
||||||
|
|
||||||
import (
|
import (
|
||||||
`context`
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
`fmt`
|
||||||
"io/fs"
|
`io/fs`
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
`sort`
|
`strings`
|
||||||
"strings"
|
|
||||||
`sync`
|
|
||||||
`time`
|
|
||||||
|
|
||||||
// "syscall"
|
// "syscall"
|
||||||
|
|
||||||
`github.com/djherbis/times`
|
|
||||||
`r00t2.io/goutils/bitmask`
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -90,81 +83,10 @@ func ExpandHome(path *string) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
GetFirst is the file equivalent of envs.GetFirst.
|
|
||||||
|
|
||||||
It iterates through paths, normalizing them along the way
|
|
||||||
(so abstracted paths such as ~/foo/bar.txt and relative paths
|
|
||||||
such as bar/baz.txt will still work), and returns the content
|
|
||||||
of the first found existing file. If the first found path
|
|
||||||
is a directory, content will be nil but isDir will be true
|
|
||||||
(as will ok).
|
|
||||||
|
|
||||||
If no path exists, ok will be false.
|
|
||||||
|
|
||||||
As always, results are not guaranteed due to permissions, etc.
|
|
||||||
potentially returning an inaccurate result.
|
|
||||||
|
|
||||||
This is a thin wrapper around GetFirstWithRef.
|
|
||||||
*/
|
|
||||||
func GetFirst(paths []string) (content []byte, isDir, ok bool) {
|
|
||||||
|
|
||||||
content, isDir, ok, _ = GetFirstWithRef(paths)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
GetFirstWithRef is the file equivalent of envs.GetFirstWithRef.
|
|
||||||
|
|
||||||
It behaves exactly like GetFirst, but with an additional returned value, idx,
|
|
||||||
which specifies the index in paths in which a path was found.
|
|
||||||
|
|
||||||
As always, results are not guaranteed due to permissions, etc.
|
|
||||||
potentially returning an inaccurate result.
|
|
||||||
*/
|
|
||||||
func GetFirstWithRef(paths []string) (content []byte, isDir, ok bool, idx int) {
|
|
||||||
|
|
||||||
var locPaths []string
|
|
||||||
var exists bool
|
|
||||||
var stat os.FileInfo
|
|
||||||
var err error
|
|
||||||
|
|
||||||
idx = -1
|
|
||||||
// We have to be a little less cavalier about this.
|
|
||||||
if paths == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
locPaths = make([]string, len(paths))
|
|
||||||
locPaths = paths[:] // Create an explicit copy so we don't modify paths.
|
|
||||||
for i, p := range locPaths {
|
|
||||||
if exists, stat, err = RealPathExistsStat(&p); err != nil {
|
|
||||||
err = nil
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !exists {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
isDir = stat.IsDir()
|
|
||||||
if !isDir {
|
|
||||||
if content, err = os.ReadFile(p); err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ok = true
|
|
||||||
idx = i
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
MakeDirIfNotExist will create a directory at a given path if it doesn't exist.
|
MakeDirIfNotExist will create a directory at a given path if it doesn't exist.
|
||||||
|
|
||||||
See also the documentation for RealPath.
|
See also the documentation for RealPath.
|
||||||
|
|
||||||
This is a bit more sane option than os.MkdirAll as it will normalize paths a little better.
|
|
||||||
*/
|
*/
|
||||||
func MakeDirIfNotExist(path string) (err error) {
|
func MakeDirIfNotExist(path string) (err error) {
|
||||||
|
|
||||||
@ -191,8 +113,6 @@ func MakeDirIfNotExist(path string) (err error) {
|
|||||||
if !stat.Mode().IsDir() {
|
if !stat.Mode().IsDir() {
|
||||||
err = errors.New(fmt.Sprintf("path %v exists but is not a directory", locPath))
|
err = errors.New(fmt.Sprintf("path %v exists but is not a directory", locPath))
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This should probably never happen. Probably.
|
// This should probably never happen. Probably.
|
||||||
@ -274,236 +194,3 @@ func RealPathExistsStat(path *string) (exists bool, stat os.FileInfo, err error)
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// SearchFsPaths gets a file/directory/etc. path list based on the provided criteria.
|
|
||||||
func SearchFsPaths(matcher FsSearchCriteria) (found, miss []*FsSearchResult, err error) {
|
|
||||||
|
|
||||||
var matched *FsSearchResult
|
|
||||||
var missed *FsSearchResult
|
|
||||||
|
|
||||||
if err = RealPath(&matcher.Root); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = filepath.WalkDir(
|
|
||||||
matcher.Root,
|
|
||||||
func(path string, d fs.DirEntry, inErr error) (outErr error) {
|
|
||||||
|
|
||||||
if inErr != nil {
|
|
||||||
outErr = inErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if matched, missed, outErr = matcher.Match(path, d, nil); outErr != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if matched != nil && !matcher.NoMatch {
|
|
||||||
found = append(found, matched)
|
|
||||||
}
|
|
||||||
if missed != nil && !matcher.NoMismatch {
|
|
||||||
miss = append(miss, missed)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
},
|
|
||||||
); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if found == nil || len(found) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// And sort them.
|
|
||||||
sort.Slice(
|
|
||||||
found,
|
|
||||||
func(i, j int) (isLess bool) {
|
|
||||||
isLess = found[i].Path < found[j].Path
|
|
||||||
return
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
SearchFsPathsAsync is exactly like SearchFsPaths, but dispatches off concurrent
|
|
||||||
workers for the filtering logic instead of performing iteratively/recursively.
|
|
||||||
It may, in some cases, be *slightly more* performant and *slightly less* in others.
|
|
||||||
Note that unlike SearchFsPaths, the results written to the
|
|
||||||
FsSearchCriteriaAsync.ResChan are not guaranteed to be in any predictable order.
|
|
||||||
|
|
||||||
All channels are expected to have already been initialized by the caller.
|
|
||||||
They will not be closed by this function.
|
|
||||||
*/
|
|
||||||
func SearchFsPathsAsync(matcher FsSearchCriteriaAsync) {
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var wgLocal sync.WaitGroup
|
|
||||||
var doneChan chan bool = make(chan bool, 1)
|
|
||||||
|
|
||||||
if matcher.ErrChan == nil {
|
|
||||||
panic(ErrNilErrChan)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if matcher.WG == nil {
|
|
||||||
matcher.ErrChan <- ErrNilWg
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer matcher.WG.Done()
|
|
||||||
|
|
||||||
if matcher.ResChan == nil && !matcher.NoMatch {
|
|
||||||
matcher.ErrChan <- ErrNilMatchChan
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if matcher.MismatchChan == nil && !matcher.NoMismatch {
|
|
||||||
matcher.ErrChan <- ErrNilMismatchChan
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = RealPath(&matcher.Root); err != nil {
|
|
||||||
matcher.ErrChan <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if matcher.Semaphore != nil && matcher.SemaphoreCtx == nil {
|
|
||||||
matcher.SemaphoreCtx = context.Background()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = filepath.WalkDir(
|
|
||||||
matcher.Root,
|
|
||||||
func(path string, de fs.DirEntry, inErr error) (outErr error) {
|
|
||||||
|
|
||||||
if inErr != nil {
|
|
||||||
inErr = filterNoFileDir(inErr)
|
|
||||||
if inErr != nil {
|
|
||||||
outErr = inErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wgLocal.Add(1)
|
|
||||||
if matcher.Semaphore != nil {
|
|
||||||
if err = matcher.Semaphore.Acquire(matcher.SemaphoreCtx, 1); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
go func(p string, d fs.DirEntry) {
|
|
||||||
var pErr error
|
|
||||||
var pResMatch *FsSearchResult
|
|
||||||
var pResMiss *FsSearchResult
|
|
||||||
|
|
||||||
defer wgLocal.Done()
|
|
||||||
|
|
||||||
if matcher.Semaphore != nil {
|
|
||||||
defer matcher.Semaphore.Release(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if pResMatch, pResMiss, pErr = matcher.Match(p, d, nil); pErr != nil {
|
|
||||||
matcher.ErrChan <- pErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if pResMatch != nil && !matcher.NoMatch {
|
|
||||||
matcher.ResChan <- pResMatch
|
|
||||||
}
|
|
||||||
if pResMiss != nil && !matcher.NoMismatch {
|
|
||||||
matcher.MismatchChan <- pResMiss
|
|
||||||
}
|
|
||||||
}(path, de)
|
|
||||||
|
|
||||||
return
|
|
||||||
},
|
|
||||||
); err != nil {
|
|
||||||
err = filterNoFileDir(err)
|
|
||||||
if err != nil {
|
|
||||||
matcher.ErrChan <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
wgLocal.Wait()
|
|
||||||
doneChan <- true
|
|
||||||
}()
|
|
||||||
|
|
||||||
<-doneChan
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
filterTimes checks a times.Timespec of a file using:
|
|
||||||
* an age specified by the caller
|
|
||||||
* an ageType bitmask for types of times to compare
|
|
||||||
* an olderThan bool (if false, the file must be younger than)
|
|
||||||
* an optional "now" timestamp for the age derivation.
|
|
||||||
*/
|
|
||||||
func filterTimes(tspec times.Timespec, age *time.Duration, ageType *pathTimeType, olderThan bool, now *time.Time) (include bool) {
|
|
||||||
|
|
||||||
var curAge time.Duration
|
|
||||||
var mask *bitmask.MaskBit
|
|
||||||
var tfunc func(t *time.Duration) (match bool) = func(t *time.Duration) (match bool) {
|
|
||||||
if olderThan {
|
|
||||||
match = *t > *age
|
|
||||||
} else {
|
|
||||||
match = *t < *age
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if tspec == nil || age == nil || ageType == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mask = ageType.Mask()
|
|
||||||
|
|
||||||
if now == nil {
|
|
||||||
now = new(time.Time)
|
|
||||||
*now = time.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
// BTIME (if supported)
|
|
||||||
if tspec.HasBirthTime() && (mask.HasFlag(bitmask.MaskBit(TimeAny)) || mask.HasFlag(bitmask.MaskBit(TimeCreated))) {
|
|
||||||
curAge = now.Sub(tspec.BirthTime())
|
|
||||||
if include = tfunc(&curAge); include {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// MTIME
|
|
||||||
if mask.HasFlag(bitmask.MaskBit(TimeAny)) || mask.HasFlag(bitmask.MaskBit(TimeModified)) {
|
|
||||||
curAge = now.Sub(tspec.ModTime())
|
|
||||||
if include = tfunc(&curAge); include {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// CTIME (if supported)
|
|
||||||
if tspec.HasChangeTime() && (mask.HasFlag(bitmask.MaskBit(TimeAny)) || mask.HasFlag(bitmask.MaskBit(TimeChanged))) {
|
|
||||||
curAge = now.Sub(tspec.ChangeTime())
|
|
||||||
if include = tfunc(&curAge); include {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ATIME
|
|
||||||
if mask.HasFlag(bitmask.MaskBit(TimeAny)) || mask.HasFlag(bitmask.MaskBit(TimeAccessed)) {
|
|
||||||
curAge = now.Sub(tspec.AccessTime())
|
|
||||||
if include = tfunc(&curAge); include {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func filterNoFileDir(err error) (filtered error) {
|
|
||||||
|
|
||||||
filtered = err
|
|
||||||
if errors.Is(err, fs.ErrNotExist) {
|
|
||||||
filtered = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
@ -1,125 +0,0 @@
|
|||||||
package paths
|
|
||||||
|
|
||||||
import (
|
|
||||||
`io/fs`
|
|
||||||
`os`
|
|
||||||
`path/filepath`
|
|
||||||
`time`
|
|
||||||
|
|
||||||
`github.com/djherbis/times`
|
|
||||||
`r00t2.io/goutils/bitmask`
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
Match returns match (a ptr to a FsSearchResult if the specified path matches, otherwise nil),
|
|
||||||
miss (ptr the specified path does not match, otherwise nil), and an fs.DirEntry and fs.FileInfo
|
|
||||||
for path. d and/or fi may be nil.
|
|
||||||
|
|
||||||
If err is not nil, it represents an unexpected error and as such, both match and miss should be nil.
|
|
||||||
|
|
||||||
Match, miss, and err will all be nil if the filesystem object/path does not exist.
|
|
||||||
*/
|
|
||||||
func (f *FsSearchCriteria) Match(path string, d fs.DirEntry, fi fs.FileInfo) (match, miss *FsSearchResult, err error) {
|
|
||||||
|
|
||||||
var typeMode fs.FileMode
|
|
||||||
var m FsSearchResult
|
|
||||||
var typeFilter *bitmask.MaskBit = bitmask.NewMaskBitExplicit(uint(f.TargetType))
|
|
||||||
|
|
||||||
m = FsSearchResult{
|
|
||||||
Path: path,
|
|
||||||
DirEntry: d,
|
|
||||||
FileInfo: fi,
|
|
||||||
Criteria: f,
|
|
||||||
}
|
|
||||||
|
|
||||||
if f == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// A DirEntry can be created from a FileInfo but not vice versa.
|
|
||||||
if m.FileInfo == nil {
|
|
||||||
if m.DirEntry != nil {
|
|
||||||
if m.FileInfo, err = m.DirEntry.Info(); err != nil {
|
|
||||||
err = filterNoFileDir(err)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if f.FollowSymlinks {
|
|
||||||
if m.FileInfo, err = os.Stat(path); err != nil {
|
|
||||||
err = filterNoFileDir(err)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if m.FileInfo, err = os.Lstat(path); err != nil {
|
|
||||||
err = filterNoFileDir(err)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m.DirEntry = fs.FileInfoToDirEntry(m.FileInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if m.DirEntry == nil {
|
|
||||||
m.DirEntry = fs.FileInfoToDirEntry(m.FileInfo)
|
|
||||||
}
|
|
||||||
if m.DirEntry == nil || m.FileInfo == nil {
|
|
||||||
m.MissReason = MissNoMeta
|
|
||||||
miss = &m
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.Times, err = times.Stat(path); err != nil {
|
|
||||||
err = filterNoFileDir(err)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.PathPtrn != nil && !f.PathPtrn.MatchString(path) {
|
|
||||||
m.MissReason = MissBadPath
|
|
||||||
miss = &m
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if f.BasePtrn != nil && !f.BasePtrn.MatchString(filepath.Base(path)) {
|
|
||||||
m.MissReason = MissBadBase
|
|
||||||
miss = &m
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// age
|
|
||||||
if f.Age != nil {
|
|
||||||
if f.Now == nil {
|
|
||||||
f.Now = new(time.Time)
|
|
||||||
*f.Now = time.Now()
|
|
||||||
}
|
|
||||||
if !filterTimes(m.Times, f.Age, &f.AgeType, f.OlderThan, f.Now) {
|
|
||||||
m.MissReason = MissBadTime
|
|
||||||
miss = &m
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fs object type (file, dir, etc.)
|
|
||||||
typeMode = m.FileInfo.Mode().Type()
|
|
||||||
if typeMode == 0 && f.NoFiles {
|
|
||||||
m.MissReason = MissFile
|
|
||||||
miss = &m
|
|
||||||
return
|
|
||||||
} else if typeMode != 0 {
|
|
||||||
if !typeFilter.HasFlag(bitmask.MaskBit(typeMode)) {
|
|
||||||
m.MissReason = MissType
|
|
||||||
miss = &m
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it gets to here, it matches.
|
|
||||||
match = &m
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package paths
|
|
||||||
|
|
||||||
import (
|
|
||||||
`r00t2.io/goutils/bitmask`
|
|
||||||
)
|
|
||||||
|
|
||||||
// Mask returns a bitmask.MaskBit from a pathTimeType.
|
|
||||||
func (p *pathTimeType) Mask() (mask *bitmask.MaskBit) {
|
|
||||||
|
|
||||||
mask = bitmask.NewMaskBitExplicit(uint(*p))
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
136
paths/types.go
136
paths/types.go
@ -1,136 +0,0 @@
|
|||||||
package paths
|
|
||||||
|
|
||||||
import (
|
|
||||||
`context`
|
|
||||||
`io/fs`
|
|
||||||
`regexp`
|
|
||||||
`sync`
|
|
||||||
`time`
|
|
||||||
|
|
||||||
`github.com/djherbis/times`
|
|
||||||
`golang.org/x/sync/semaphore`
|
|
||||||
`r00t2.io/goutils/bitmask`
|
|
||||||
)
|
|
||||||
|
|
||||||
// FsSearchCriteria contains filter criteria for SearchFsPaths* functions.
|
|
||||||
type FsSearchCriteria struct {
|
|
||||||
// Root indicates the root to search.
|
|
||||||
Root string `json:"root" toml:"RootPath" yaml:"Root Path" xml:"root,attr" validate:"dir"`
|
|
||||||
// NoMatch, if true, will not return matches. If NoMatch and NoMismatch are both true, no results will be returned.
|
|
||||||
NoMatch bool `json:"no_match" toml:"NoMatch" yaml:"No Matches" xml:"noMatch,attr"`
|
|
||||||
// NoMismatch, if true, will not return mismatches. If NoMatch and NoMismatch are both true, no results will be returned.
|
|
||||||
NoMismatch bool `json:"no_miss" toml:"NoMismatch" yaml:"No Mismatches" xml:"noMiss,attr"`
|
|
||||||
/*
|
|
||||||
TargetType defines what types of filesystem objects should be matched.
|
|
||||||
It can consist of one or more (io/)fs.FileMode types OR'd together
|
|
||||||
(ensure they are part of (io/)fs.ModeType).
|
|
||||||
(You can use 0 to match regular files explicitly, and/or NoFiles = true to exclude them.)
|
|
||||||
*/
|
|
||||||
TargetType fs.FileMode `json:"type_tgt" toml:"TargetType" yaml:"Target Type" xml:"typeTgt,attr"`
|
|
||||||
// NoFiles excludes files from TargetType-matching (as there isn't a way to explicitly exclude files otherwise if a non-zero mode is given).
|
|
||||||
NoFiles bool `json:"no_file" toml:"ExcludeFiles" yaml:"Exclude Files" xml:"noFile,attr"`
|
|
||||||
// FollowSymlinks, if true and a path being tested is a symlink, will use metadata (age, etc.) of the symlink itself rather than the link target.
|
|
||||||
FollowSymlinks bool `json:"follow_sym" toml:"FollowSymlinks" yaml:"Follow Symlinks" xml:"followSym,attr"`
|
|
||||||
// BasePtrn, if specified, will apply to the *base name (that is, quux.txt rather than /foo/bar/baz/quux.txt). See also PathPtrn.
|
|
||||||
BasePtrn *regexp.Regexp `json:"ptrn_base,omitempty" toml:"BaseNamePattern,omitempty" yaml:"Base Name Pattern,omitempty" xml:"ptrnBase,attr,omitempty"`
|
|
||||||
// PathPtrn, if specified, will apply to the *full path* (e.g. /foo/bar/baz/quux.txt, not just quux.txt). See also BasePtrn.
|
|
||||||
PathPtrn *regexp.Regexp `json:"ptrn_path,omitempty" toml:"PathPattern,omitempty" yaml:"Path Pattern,omitempty" xml:"ptrnPath,attr,omitempty"`
|
|
||||||
/*
|
|
||||||
Age, if specified, indicates the comparison of Now againt the AgeType of filesystem objects.
|
|
||||||
Use OlderThan to indicate if it should be older or newer.
|
|
||||||
*/
|
|
||||||
Age *time.Duration `json:"age,omitempty" toml:"Age,omitempty" yaml:"Age,omitempty" xml:"age,attr,omitempty"`
|
|
||||||
/*
|
|
||||||
AgeType can be one (or more, OR'd together) of the Time* constants in this package (TimeAny, TimeAccessed, TimeCreated,
|
|
||||||
TimeChanged, TimeModified) to indicate what timestamp(s) to use for comparing Age.
|
|
||||||
|
|
||||||
The zero-value is TimeAny.
|
|
||||||
|
|
||||||
The first matching timestamp will pass all time comparisons.
|
|
||||||
Be mindful of timestamp type support/limitations per OS/filesystem of Root.
|
|
||||||
|
|
||||||
Completely unused if Age is nil.
|
|
||||||
*/
|
|
||||||
AgeType pathTimeType `json:"type_age" toml:"AgeType" yaml:"Age Type" xml:"typeAge,attr"`
|
|
||||||
/*
|
|
||||||
OlderThan, if true (and Age is not nil), indicates that matching filesystem objects should have their
|
|
||||||
AgeType older than Now. If false, their AgeType should be *newer* than Now.
|
|
||||||
|
|
||||||
Completely unused if Age is nil.
|
|
||||||
*/
|
|
||||||
OlderThan bool `json:"older" toml:"OlderThan" yaml:"Older Than" xml:"older,attr"`
|
|
||||||
/*
|
|
||||||
Now expresses a time to compare to Age via AgeType and OlderThan.
|
|
||||||
Note that it may be any valid time, not necessarily "now".
|
|
||||||
If Age is specified but Now is nil, it will be populated with time.Now() when the search is invoked.
|
|
||||||
|
|
||||||
Completely unused if Age is nil.
|
|
||||||
*/
|
|
||||||
Now *time.Time `json:"now,omitempty" toml:"Now,omitempty" yaml:"Now,omitempty" xml:"now,attr,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// FsSearchCriteriaAsync extends FsSearchCriteria for use in an asynchronous (goroutine) manner.
|
|
||||||
type FsSearchCriteriaAsync struct {
|
|
||||||
FsSearchCriteria
|
|
||||||
/*
|
|
||||||
WG should be a non-nil pointer to a sync.WaitGroup.
|
|
||||||
This is used to manage searching completion to the caller.
|
|
||||||
|
|
||||||
.Done() will be called once within the search function, but no .Add() will be called;
|
|
||||||
.Add() should be done by the caller beforehand.
|
|
||||||
*/
|
|
||||||
WG *sync.WaitGroup
|
|
||||||
// ResChan must be a non-nil channel for (positive) match results to be sent to.
|
|
||||||
ResChan chan *FsSearchResult
|
|
||||||
// MismatchChan, if not nil, will have negative matches/"misses" sent to it.
|
|
||||||
MismatchChan chan *FsSearchResult
|
|
||||||
/*
|
|
||||||
ErrChan should be a non-nil error channel for any unexpected errors encountered.
|
|
||||||
|
|
||||||
If nil, a panic will be raised.
|
|
||||||
*/
|
|
||||||
ErrChan chan error
|
|
||||||
/*
|
|
||||||
Semaphore is completely optional, but if non-nil
|
|
||||||
it will be used to limit concurrent filesystem
|
|
||||||
object processing.
|
|
||||||
|
|
||||||
It is generally a Very Good Idea(TM) to use this,
|
|
||||||
as the default is to dispatch all processing concurrently.
|
|
||||||
This can lead to some heavy I/O and CPU wait.
|
|
||||||
|
|
||||||
(See https://pkg.go.dev/golang.org/x/sync/semaphore for details.)
|
|
||||||
*/
|
|
||||||
Semaphore *semaphore.Weighted
|
|
||||||
/*
|
|
||||||
SemaphoreCtx is the context.Context to use for Semaphore.
|
|
||||||
If nil (but Sempaphore is not), one will be created locally/internally.
|
|
||||||
*/
|
|
||||||
SemaphoreCtx context.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
// FsSearchResult contains a match/miss result for FsSearchCriteria and FsSearchCriteriaAsync.
|
|
||||||
type FsSearchResult struct {
|
|
||||||
/*
|
|
||||||
Path is the path to the object on the filesystem.
|
|
||||||
It may or may not exist at the time of return,
|
|
||||||
but will not be an empty string.
|
|
||||||
*/
|
|
||||||
Path string `json:"path" toml:"Path" yaml:"Path" xml:"path,attr"`
|
|
||||||
// DirEntry is the fs.DirEntry for the Path; note that .Name() is the base name only. TODO: serialization?
|
|
||||||
DirEntry fs.DirEntry `json:"-" toml:"-" yaml:"-" xml:"-"`
|
|
||||||
// FileInfo is the fs.FileInfo for the Path; note that .Name() is the base name only. TODO: serialization?
|
|
||||||
FileInfo fs.FileInfo `json:"-" toml:"-" yaml:"-" xml:"-"`
|
|
||||||
// Criteria is the evaluated criteria specified that this FsSearchResult matched.
|
|
||||||
Criteria *FsSearchCriteria `json:"criteria" toml:"Criteria" yaml:"Criteria" xml:"criteria"`
|
|
||||||
// Times holds the mtime, ctime, etc. of the filesystem object (where supported). TODO: serialization?
|
|
||||||
Times times.Timespec `json:"-" toml:"-" yaml:"-" xml:"-"`
|
|
||||||
// MissReason contains the reason the result is a miss (MissNoMiss if a match); see the Miss* constants.
|
|
||||||
MissReason missReason `json:"miss_reason" toml:"MissReason" yaml:"Miss Reason" xml:"miss,attr"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type missReason string
|
|
||||||
|
|
||||||
type pathMode bitmask.MaskBit
|
|
||||||
|
|
||||||
type pathTimeType bitmask.MaskBit
|
|
@ -1,53 +0,0 @@
|
|||||||
package sysutils
|
|
||||||
|
|
||||||
import (
|
|
||||||
`golang.org/x/sys/unix`
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
IDState collects information about the current running process.
|
|
||||||
It should only be used as returned from GetIDState().
|
|
||||||
Its methods WILL return false information if any of these values are altered.
|
|
||||||
|
|
||||||
FSUID/FSGID are not supported.
|
|
||||||
*/
|
|
||||||
type IDState struct {
|
|
||||||
// RUID: Real UID
|
|
||||||
RUID int
|
|
||||||
// EUID: Effective UID
|
|
||||||
EUID int
|
|
||||||
// SUID: Saved Set UID
|
|
||||||
SUID int
|
|
||||||
// RGID: Real GID
|
|
||||||
RGID int
|
|
||||||
// EGID: Effective GID
|
|
||||||
EGID int
|
|
||||||
// SGID: Saved Set GID
|
|
||||||
SGID int
|
|
||||||
// SudoEnvUser is true if SUDO_USER or SUDO_UID is set.
|
|
||||||
SudoEnvUser bool
|
|
||||||
// SudoEnvGroup is true if SUDO_GID is set.
|
|
||||||
SudoEnvGroup bool
|
|
||||||
// SudoEnvCmd is true if SUDO_COMMAND is set.
|
|
||||||
SudoEnvCmd bool
|
|
||||||
// SudoEnvHome is true if SUDO_HOME is set.
|
|
||||||
SudoEnvHome bool
|
|
||||||
// SudoEnvVars is true if any of the "well-known" sudo environment variables are set.
|
|
||||||
SudoEnvVars bool
|
|
||||||
// PPIDUidMatch is true if the parent PID UID matches the current process UID (mismatch usually indicates sudo invocation).
|
|
||||||
PPIDUidMatch bool
|
|
||||||
// PPIDGidMatch is true if the parent PID GID matches the current process GID (mismatch usually indicates sudo invocation).
|
|
||||||
PPIDGidMatch bool
|
|
||||||
// uidsChecked is true if the RUID, EUID, and SUID have been populated. (They will be 0 if unset OR if root.)
|
|
||||||
uidsChecked bool
|
|
||||||
// gidsChecked is true if the RGID, EGID, and SGID have been populated. (They will be 0 if unset OR if root.)
|
|
||||||
gidsChecked bool
|
|
||||||
// sudoChecked is true if the SudoEnvVars is set.
|
|
||||||
sudoChecked bool
|
|
||||||
// ppidUidChecked is true if the PPIDUidMatch is set.
|
|
||||||
ppidUidChecked bool
|
|
||||||
// ppidGidChecked is true if the PPIDGidMatch is set.
|
|
||||||
ppidGidChecked bool
|
|
||||||
// stat holds the stat information for the parent PID.
|
|
||||||
stat *unix.Stat_t
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user