deserialize.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. // Copyright 2015 CoreOS, Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package unit
  15. import (
  16. "bufio"
  17. "bytes"
  18. "errors"
  19. "fmt"
  20. "io"
  21. "strings"
  22. "unicode"
  23. )
  24. const (
  25. // SYSTEMD_LINE_MAX mimics the maximum line length that systemd can use.
  26. // On typical systemd platforms (i.e. modern Linux), this will most
  27. // commonly be 2048, so let's use that as a sanity check.
  28. // Technically, we should probably pull this at runtime:
  29. // SYSTEMD_LINE_MAX = int(C.sysconf(C.__SC_LINE_MAX))
  30. // but this would introduce an (unfortunate) dependency on cgo
  31. SYSTEMD_LINE_MAX = 2048
  32. // characters that systemd considers indicate a newline
  33. SYSTEMD_NEWLINE = "\r\n"
  34. )
  35. var (
  36. ErrLineTooLong = fmt.Errorf("line too long (max %d bytes)", SYSTEMD_LINE_MAX)
  37. )
  38. // Deserialize parses a systemd unit file into a list of UnitOption objects.
  39. func Deserialize(f io.Reader) (opts []*UnitOption, err error) {
  40. lexer, optchan, errchan := newLexer(f)
  41. go lexer.lex()
  42. for opt := range optchan {
  43. opts = append(opts, &(*opt))
  44. }
  45. err = <-errchan
  46. return opts, err
  47. }
  48. func newLexer(f io.Reader) (*lexer, <-chan *UnitOption, <-chan error) {
  49. optchan := make(chan *UnitOption)
  50. errchan := make(chan error, 1)
  51. buf := bufio.NewReader(f)
  52. return &lexer{buf, optchan, errchan, ""}, optchan, errchan
  53. }
  54. type lexer struct {
  55. buf *bufio.Reader
  56. optchan chan *UnitOption
  57. errchan chan error
  58. section string
  59. }
  60. func (l *lexer) lex() {
  61. var err error
  62. defer func() {
  63. close(l.optchan)
  64. close(l.errchan)
  65. }()
  66. next := l.lexNextSection
  67. for next != nil {
  68. if l.buf.Buffered() >= SYSTEMD_LINE_MAX {
  69. // systemd truncates lines longer than LINE_MAX
  70. // https://bugs.freedesktop.org/show_bug.cgi?id=85308
  71. // Rather than allowing this to pass silently, let's
  72. // explicitly gate people from encountering this
  73. line, err := l.buf.Peek(SYSTEMD_LINE_MAX)
  74. if err != nil {
  75. l.errchan <- err
  76. return
  77. }
  78. if bytes.IndexAny(line, SYSTEMD_NEWLINE) == -1 {
  79. l.errchan <- ErrLineTooLong
  80. return
  81. }
  82. }
  83. next, err = next()
  84. if err != nil {
  85. l.errchan <- err
  86. return
  87. }
  88. }
  89. }
  90. type lexStep func() (lexStep, error)
  91. func (l *lexer) lexSectionName() (lexStep, error) {
  92. sec, err := l.buf.ReadBytes(']')
  93. if err != nil {
  94. return nil, errors.New("unable to find end of section")
  95. }
  96. return l.lexSectionSuffixFunc(string(sec[:len(sec)-1])), nil
  97. }
  98. func (l *lexer) lexSectionSuffixFunc(section string) lexStep {
  99. return func() (lexStep, error) {
  100. garbage, _, err := l.toEOL()
  101. if err != nil {
  102. return nil, err
  103. }
  104. garbage = bytes.TrimSpace(garbage)
  105. if len(garbage) > 0 {
  106. return nil, fmt.Errorf("found garbage after section name %s: %v", l.section, garbage)
  107. }
  108. return l.lexNextSectionOrOptionFunc(section), nil
  109. }
  110. }
  111. func (l *lexer) ignoreLineFunc(next lexStep) lexStep {
  112. return func() (lexStep, error) {
  113. for {
  114. line, _, err := l.toEOL()
  115. if err != nil {
  116. return nil, err
  117. }
  118. line = bytes.TrimSuffix(line, []byte{' '})
  119. // lack of continuation means this line has been exhausted
  120. if !bytes.HasSuffix(line, []byte{'\\'}) {
  121. break
  122. }
  123. }
  124. // reached end of buffer, safe to exit
  125. return next, nil
  126. }
  127. }
  128. func (l *lexer) lexNextSection() (lexStep, error) {
  129. r, _, err := l.buf.ReadRune()
  130. if err != nil {
  131. if err == io.EOF {
  132. err = nil
  133. }
  134. return nil, err
  135. }
  136. if r == '[' {
  137. return l.lexSectionName, nil
  138. } else if isComment(r) {
  139. return l.ignoreLineFunc(l.lexNextSection), nil
  140. }
  141. return l.lexNextSection, nil
  142. }
  143. func (l *lexer) lexNextSectionOrOptionFunc(section string) lexStep {
  144. return func() (lexStep, error) {
  145. r, _, err := l.buf.ReadRune()
  146. if err != nil {
  147. if err == io.EOF {
  148. err = nil
  149. }
  150. return nil, err
  151. }
  152. if unicode.IsSpace(r) {
  153. return l.lexNextSectionOrOptionFunc(section), nil
  154. } else if r == '[' {
  155. return l.lexSectionName, nil
  156. } else if isComment(r) {
  157. return l.ignoreLineFunc(l.lexNextSectionOrOptionFunc(section)), nil
  158. }
  159. l.buf.UnreadRune()
  160. return l.lexOptionNameFunc(section), nil
  161. }
  162. }
  163. func (l *lexer) lexOptionNameFunc(section string) lexStep {
  164. return func() (lexStep, error) {
  165. var partial bytes.Buffer
  166. for {
  167. r, _, err := l.buf.ReadRune()
  168. if err != nil {
  169. return nil, err
  170. }
  171. if r == '\n' || r == '\r' {
  172. return nil, errors.New("unexpected newline encountered while parsing option name")
  173. }
  174. if r == '=' {
  175. break
  176. }
  177. partial.WriteRune(r)
  178. }
  179. name := strings.TrimSpace(partial.String())
  180. return l.lexOptionValueFunc(section, name, bytes.Buffer{}), nil
  181. }
  182. }
  183. func (l *lexer) lexOptionValueFunc(section, name string, partial bytes.Buffer) lexStep {
  184. return func() (lexStep, error) {
  185. for {
  186. line, eof, err := l.toEOL()
  187. if err != nil {
  188. return nil, err
  189. }
  190. if len(bytes.TrimSpace(line)) == 0 {
  191. break
  192. }
  193. partial.Write(line)
  194. // lack of continuation means this value has been exhausted
  195. idx := bytes.LastIndex(line, []byte{'\\'})
  196. if idx == -1 || idx != (len(line)-1) {
  197. break
  198. }
  199. if !eof {
  200. partial.WriteRune('\n')
  201. }
  202. return l.lexOptionValueFunc(section, name, partial), nil
  203. }
  204. val := partial.String()
  205. if strings.HasSuffix(val, "\n") {
  206. // A newline was added to the end, so the file didn't end with a backslash.
  207. // => Keep the newline
  208. val = strings.TrimSpace(val) + "\n"
  209. } else {
  210. val = strings.TrimSpace(val)
  211. }
  212. l.optchan <- &UnitOption{Section: section, Name: name, Value: val}
  213. return l.lexNextSectionOrOptionFunc(section), nil
  214. }
  215. }
  216. // toEOL reads until the end-of-line or end-of-file.
  217. // Returns (data, EOFfound, error)
  218. func (l *lexer) toEOL() ([]byte, bool, error) {
  219. line, err := l.buf.ReadBytes('\n')
  220. // ignore EOF here since it's roughly equivalent to EOL
  221. if err != nil && err != io.EOF {
  222. return nil, false, err
  223. }
  224. line = bytes.TrimSuffix(line, []byte{'\r'})
  225. line = bytes.TrimSuffix(line, []byte{'\n'})
  226. return line, err == io.EOF, nil
  227. }
  228. func isComment(r rune) bool {
  229. return r == '#' || r == ';'
  230. }