This commit is contained in:
		
							parent
							
								
									0b0a67cc33
								
							
						
					
					
						commit
						5121acdc58
					
				
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| 
						 | 
				
			
			@ -0,0 +1,46 @@
 | 
			
		|||
# Names should be added to this file as
 | 
			
		||||
#	Name or Organization <email address>
 | 
			
		||||
# The email address is not required for organizations.
 | 
			
		||||
 | 
			
		||||
# You can update this list using the following command:
 | 
			
		||||
#
 | 
			
		||||
#   $ git shortlog -se | awk '{print $2 " " $3 " " $4}'
 | 
			
		||||
 | 
			
		||||
# Please keep the list sorted.
 | 
			
		||||
 | 
			
		||||
Adrien Bustany <adrien@bustany.org>
 | 
			
		||||
Amit Krishnan <amit.krishnan@oracle.com>
 | 
			
		||||
Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
 | 
			
		||||
Bruno Bigras <bigras.bruno@gmail.com>
 | 
			
		||||
Caleb Spare <cespare@gmail.com>
 | 
			
		||||
Case Nelson <case@teammating.com>
 | 
			
		||||
Chris Howey <chris@howey.me> <howeyc@gmail.com>
 | 
			
		||||
Christoffer Buchholz <christoffer.buchholz@gmail.com>
 | 
			
		||||
Daniel Wagner-Hall <dawagner@gmail.com>
 | 
			
		||||
Dave Cheney <dave@cheney.net>
 | 
			
		||||
Evan Phoenix <evan@fallingsnow.net>
 | 
			
		||||
Francisco Souza <f@souza.cc>
 | 
			
		||||
Hari haran <hariharan.uno@gmail.com>
 | 
			
		||||
John C Barstow
 | 
			
		||||
Kelvin Fo <vmirage@gmail.com>
 | 
			
		||||
Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp>
 | 
			
		||||
Matt Layher <mdlayher@gmail.com>
 | 
			
		||||
Nathan Youngman <git@nathany.com>
 | 
			
		||||
Patrick <patrick@dropbox.com>
 | 
			
		||||
Paul Hammond <paul@paulhammond.org>
 | 
			
		||||
Pawel Knap <pawelknap88@gmail.com>
 | 
			
		||||
Pieter Droogendijk <pieter@binky.org.uk>
 | 
			
		||||
Pursuit92 <JoshChase@techpursuit.net>
 | 
			
		||||
Riku Voipio <riku.voipio@linaro.org>
 | 
			
		||||
Rob Figueiredo <robfig@gmail.com>
 | 
			
		||||
Slawek Ligus <root@ooz.ie>
 | 
			
		||||
Soge Zhang <zhssoge@gmail.com>
 | 
			
		||||
Tiffany Jernigan <tiffany.jernigan@intel.com>
 | 
			
		||||
Tilak Sharma <tilaks@google.com>
 | 
			
		||||
Travis Cline <travis.cline@gmail.com>
 | 
			
		||||
Tudor Golubenco <tudor.g@gmail.com>
 | 
			
		||||
Yukang <moorekang@gmail.com>
 | 
			
		||||
bronze1man <bronze1man@gmail.com>
 | 
			
		||||
debrando <denis.brandolini@gmail.com>
 | 
			
		||||
henrikedwards <henrik.edwards@gmail.com>
 | 
			
		||||
铁哥 <guotie.9@gmail.com>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,307 @@
 | 
			
		|||
# Changelog
 | 
			
		||||
 | 
			
		||||
## v1.4.2 / 2016-10-10
 | 
			
		||||
 | 
			
		||||
* Linux: use InotifyInit1 with IN_CLOEXEC to stop leaking a file descriptor to a child process when using fork/exec [#178](https://github.com/fsnotify/fsnotify/pull/178) (thanks @pattyshack)
 | 
			
		||||
 | 
			
		||||
## v1.4.1 / 2016-10-04
 | 
			
		||||
 | 
			
		||||
* Fix flaky inotify stress test on Linux [#177](https://github.com/fsnotify/fsnotify/pull/177) (thanks @pattyshack)
 | 
			
		||||
 | 
			
		||||
## v1.4.0 / 2016-10-01
 | 
			
		||||
 | 
			
		||||
* add a String() method to Event.Op [#165](https://github.com/fsnotify/fsnotify/pull/165) (thanks @oozie)
 | 
			
		||||
 | 
			
		||||
## v1.3.1 / 2016-06-28
 | 
			
		||||
 | 
			
		||||
* Windows: fix for double backslash when watching the root of a drive [#151](https://github.com/fsnotify/fsnotify/issues/151) (thanks @brunoqc)
 | 
			
		||||
 | 
			
		||||
## v1.3.0 / 2016-04-19
 | 
			
		||||
 | 
			
		||||
* Support linux/arm64 by [patching](https://go-review.googlesource.com/#/c/21971/) x/sys/unix and switching to to it from syscall (thanks @suihkulokki) [#135](https://github.com/fsnotify/fsnotify/pull/135)
 | 
			
		||||
 | 
			
		||||
## v1.2.10 / 2016-03-02
 | 
			
		||||
 | 
			
		||||
* Fix golint errors in windows.go [#121](https://github.com/fsnotify/fsnotify/pull/121) (thanks @tiffanyfj)
 | 
			
		||||
 | 
			
		||||
## v1.2.9 / 2016-01-13
 | 
			
		||||
 | 
			
		||||
kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsnotify/pull/111) (thanks @bep)
 | 
			
		||||
 | 
			
		||||
## v1.2.8 / 2015-12-17
 | 
			
		||||
 | 
			
		||||
* kqueue: fix race condition in Close [#105](https://github.com/fsnotify/fsnotify/pull/105) (thanks @djui for reporting the issue and @ppknap for writing a failing test)
 | 
			
		||||
* inotify: fix race in test
 | 
			
		||||
* enable race detection for continuous integration (Linux, Mac, Windows)
 | 
			
		||||
 | 
			
		||||
## v1.2.5 / 2015-10-17
 | 
			
		||||
 | 
			
		||||
* inotify: use epoll_create1 for arm64 support (requires Linux 2.6.27 or later) [#100](https://github.com/fsnotify/fsnotify/pull/100) (thanks @suihkulokki)
 | 
			
		||||
* inotify: fix path leaks [#73](https://github.com/fsnotify/fsnotify/pull/73) (thanks @chamaken)
 | 
			
		||||
* kqueue: watch for rename events on subdirectories [#83](https://github.com/fsnotify/fsnotify/pull/83) (thanks @guotie)
 | 
			
		||||
* kqueue: avoid infinite loops from symlinks cycles [#101](https://github.com/fsnotify/fsnotify/pull/101) (thanks @illicitonion)
 | 
			
		||||
 | 
			
		||||
## v1.2.1 / 2015-10-14
 | 
			
		||||
 | 
			
		||||
* kqueue: don't watch named pipes [#98](https://github.com/fsnotify/fsnotify/pull/98) (thanks @evanphx)
 | 
			
		||||
 | 
			
		||||
## v1.2.0 / 2015-02-08
 | 
			
		||||
 | 
			
		||||
* inotify: use epoll to wake up readEvents [#66](https://github.com/fsnotify/fsnotify/pull/66) (thanks @PieterD)
 | 
			
		||||
* inotify: closing watcher should now always shut down goroutine [#63](https://github.com/fsnotify/fsnotify/pull/63) (thanks @PieterD)
 | 
			
		||||
* kqueue: close kqueue after removing watches, fixes [#59](https://github.com/fsnotify/fsnotify/issues/59)
 | 
			
		||||
 | 
			
		||||
## v1.1.1 / 2015-02-05
 | 
			
		||||
 | 
			
		||||
* inotify: Retry read on EINTR [#61](https://github.com/fsnotify/fsnotify/issues/61) (thanks @PieterD)
 | 
			
		||||
 | 
			
		||||
## v1.1.0 / 2014-12-12
 | 
			
		||||
 | 
			
		||||
* kqueue: rework internals [#43](https://github.com/fsnotify/fsnotify/pull/43)
 | 
			
		||||
    * add low-level functions
 | 
			
		||||
    * only need to store flags on directories
 | 
			
		||||
    * less mutexes [#13](https://github.com/fsnotify/fsnotify/issues/13)
 | 
			
		||||
    * done can be an unbuffered channel
 | 
			
		||||
    * remove calls to os.NewSyscallError
 | 
			
		||||
* More efficient string concatenation for Event.String() [#52](https://github.com/fsnotify/fsnotify/pull/52) (thanks @mdlayher)
 | 
			
		||||
* kqueue: fix regression in  rework causing subdirectories to be watched [#48](https://github.com/fsnotify/fsnotify/issues/48)
 | 
			
		||||
* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51)
 | 
			
		||||
 | 
			
		||||
## v1.0.4 / 2014-09-07
 | 
			
		||||
 | 
			
		||||
* kqueue: add dragonfly to the build tags.
 | 
			
		||||
* Rename source code files, rearrange code so exported APIs are at the top.
 | 
			
		||||
* Add done channel to example code. [#37](https://github.com/fsnotify/fsnotify/pull/37) (thanks @chenyukang)
 | 
			
		||||
 | 
			
		||||
## v1.0.3 / 2014-08-19
 | 
			
		||||
 | 
			
		||||
* [Fix] Windows MOVED_TO now translates to Create like on BSD and Linux. [#36](https://github.com/fsnotify/fsnotify/issues/36)
 | 
			
		||||
 | 
			
		||||
## v1.0.2 / 2014-08-17
 | 
			
		||||
 | 
			
		||||
* [Fix] Missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
 | 
			
		||||
* [Fix] Make ./path and path equivalent. (thanks @zhsso)
 | 
			
		||||
 | 
			
		||||
## v1.0.0 / 2014-08-15
 | 
			
		||||
 | 
			
		||||
* [API] Remove AddWatch on Windows, use Add.
 | 
			
		||||
* Improve documentation for exported identifiers. [#30](https://github.com/fsnotify/fsnotify/issues/30)
 | 
			
		||||
* Minor updates based on feedback from golint.
 | 
			
		||||
 | 
			
		||||
## dev / 2014-07-09
 | 
			
		||||
 | 
			
		||||
* Moved to [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify).
 | 
			
		||||
* Use os.NewSyscallError instead of returning errno (thanks @hariharan-uno)
 | 
			
		||||
 | 
			
		||||
## dev / 2014-07-04
 | 
			
		||||
 | 
			
		||||
* kqueue: fix incorrect mutex used in Close()
 | 
			
		||||
* Update example to demonstrate usage of Op.
 | 
			
		||||
 | 
			
		||||
## dev / 2014-06-28
 | 
			
		||||
 | 
			
		||||
* [API] Don't set the Write Op for attribute notifications [#4](https://github.com/fsnotify/fsnotify/issues/4)
 | 
			
		||||
* Fix for String() method on Event (thanks Alex Brainman)
 | 
			
		||||
* Don't build on Plan 9 or Solaris (thanks @4ad)
 | 
			
		||||
 | 
			
		||||
## dev / 2014-06-21
 | 
			
		||||
 | 
			
		||||
* Events channel of type Event rather than *Event.
 | 
			
		||||
* [internal] use syscall constants directly for inotify and kqueue.
 | 
			
		||||
* [internal] kqueue: rename events to kevents and fileEvent to event.
 | 
			
		||||
 | 
			
		||||
## dev / 2014-06-19
 | 
			
		||||
 | 
			
		||||
* Go 1.3+ required on Windows (uses syscall.ERROR_MORE_DATA internally).
 | 
			
		||||
* [internal] remove cookie from Event struct (unused).
 | 
			
		||||
* [internal] Event struct has the same definition across every OS.
 | 
			
		||||
* [internal] remove internal watch and removeWatch methods.
 | 
			
		||||
 | 
			
		||||
## dev / 2014-06-12
 | 
			
		||||
 | 
			
		||||
* [API] Renamed Watch() to Add() and RemoveWatch() to Remove().
 | 
			
		||||
* [API] Pluralized channel names: Events and Errors.
 | 
			
		||||
* [API] Renamed FileEvent struct to Event.
 | 
			
		||||
* [API] Op constants replace methods like IsCreate().
 | 
			
		||||
 | 
			
		||||
## dev / 2014-06-12
 | 
			
		||||
 | 
			
		||||
* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98)
 | 
			
		||||
 | 
			
		||||
## dev / 2014-05-23
 | 
			
		||||
 | 
			
		||||
* [API] Remove current implementation of WatchFlags.
 | 
			
		||||
    * current implementation doesn't take advantage of OS for efficiency
 | 
			
		||||
    * provides little benefit over filtering events as they are received, but has  extra bookkeeping and mutexes
 | 
			
		||||
    * no tests for the current implementation
 | 
			
		||||
    * not fully implemented on Windows [#93](https://github.com/howeyc/fsnotify/issues/93#issuecomment-39285195)
 | 
			
		||||
 | 
			
		||||
## v0.9.3 / 2014-12-31
 | 
			
		||||
 | 
			
		||||
* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51)
 | 
			
		||||
 | 
			
		||||
## v0.9.2 / 2014-08-17
 | 
			
		||||
 | 
			
		||||
* [Backport] Fix missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
 | 
			
		||||
 | 
			
		||||
## v0.9.1 / 2014-06-12
 | 
			
		||||
 | 
			
		||||
* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98)
 | 
			
		||||
 | 
			
		||||
## v0.9.0 / 2014-01-17
 | 
			
		||||
 | 
			
		||||
* IsAttrib() for events that only concern a file's metadata [#79][] (thanks @abustany)
 | 
			
		||||
* [Fix] kqueue: fix deadlock [#77][] (thanks @cespare)
 | 
			
		||||
* [NOTICE] Development has moved to `code.google.com/p/go.exp/fsnotify` in preparation for inclusion in the Go standard library.
 | 
			
		||||
 | 
			
		||||
## v0.8.12 / 2013-11-13
 | 
			
		||||
 | 
			
		||||
* [API] Remove FD_SET and friends from Linux adapter
 | 
			
		||||
 | 
			
		||||
## v0.8.11 / 2013-11-02
 | 
			
		||||
 | 
			
		||||
* [Doc] Add Changelog [#72][] (thanks @nathany)
 | 
			
		||||
* [Doc] Spotlight and double modify events on macOS [#62][] (reported by @paulhammond)
 | 
			
		||||
 | 
			
		||||
## v0.8.10 / 2013-10-19
 | 
			
		||||
 | 
			
		||||
* [Fix] kqueue: remove file watches when parent directory is removed [#71][] (reported by @mdwhatcott)
 | 
			
		||||
* [Fix] kqueue: race between Close and readEvents [#70][] (reported by @bernerdschaefer)
 | 
			
		||||
* [Doc] specify OS-specific limits in README (thanks @debrando)
 | 
			
		||||
 | 
			
		||||
## v0.8.9 / 2013-09-08
 | 
			
		||||
 | 
			
		||||
* [Doc] Contributing (thanks @nathany)
 | 
			
		||||
* [Doc] update package path in example code [#63][] (thanks @paulhammond)
 | 
			
		||||
* [Doc] GoCI badge in README (Linux only) [#60][]
 | 
			
		||||
* [Doc] Cross-platform testing with Vagrant  [#59][] (thanks @nathany)
 | 
			
		||||
 | 
			
		||||
## v0.8.8 / 2013-06-17
 | 
			
		||||
 | 
			
		||||
* [Fix] Windows: handle `ERROR_MORE_DATA` on Windows [#49][] (thanks @jbowtie)
 | 
			
		||||
 | 
			
		||||
## v0.8.7 / 2013-06-03
 | 
			
		||||
 | 
			
		||||
* [API] Make syscall flags internal
 | 
			
		||||
* [Fix] inotify: ignore event changes
 | 
			
		||||
* [Fix] race in symlink test [#45][] (reported by @srid)
 | 
			
		||||
* [Fix] tests on Windows
 | 
			
		||||
* lower case error messages
 | 
			
		||||
 | 
			
		||||
## v0.8.6 / 2013-05-23
 | 
			
		||||
 | 
			
		||||
* kqueue: Use EVT_ONLY flag on Darwin
 | 
			
		||||
* [Doc] Update README with full example
 | 
			
		||||
 | 
			
		||||
## v0.8.5 / 2013-05-09
 | 
			
		||||
 | 
			
		||||
* [Fix] inotify: allow monitoring of "broken" symlinks (thanks @tsg)
 | 
			
		||||
 | 
			
		||||
## v0.8.4 / 2013-04-07
 | 
			
		||||
 | 
			
		||||
* [Fix] kqueue: watch all file events [#40][] (thanks @ChrisBuchholz)
 | 
			
		||||
 | 
			
		||||
## v0.8.3 / 2013-03-13
 | 
			
		||||
 | 
			
		||||
* [Fix] inoitfy/kqueue memory leak [#36][] (reported by @nbkolchin)
 | 
			
		||||
* [Fix] kqueue: use fsnFlags for watching a directory [#33][] (reported by @nbkolchin)
 | 
			
		||||
 | 
			
		||||
## v0.8.2 / 2013-02-07
 | 
			
		||||
 | 
			
		||||
* [Doc] add Authors
 | 
			
		||||
* [Fix] fix data races for map access [#29][] (thanks @fsouza)
 | 
			
		||||
 | 
			
		||||
## v0.8.1 / 2013-01-09
 | 
			
		||||
 | 
			
		||||
* [Fix] Windows path separators
 | 
			
		||||
* [Doc] BSD License
 | 
			
		||||
 | 
			
		||||
## v0.8.0 / 2012-11-09
 | 
			
		||||
 | 
			
		||||
* kqueue: directory watching improvements (thanks @vmirage)
 | 
			
		||||
* inotify: add `IN_MOVED_TO` [#25][] (requested by @cpisto)
 | 
			
		||||
* [Fix] kqueue: deleting watched directory [#24][] (reported by @jakerr)
 | 
			
		||||
 | 
			
		||||
## v0.7.4 / 2012-10-09
 | 
			
		||||
 | 
			
		||||
* [Fix] inotify: fixes from https://codereview.appspot.com/5418045/ (ugorji)
 | 
			
		||||
* [Fix] kqueue: preserve watch flags when watching for delete [#21][] (reported by @robfig)
 | 
			
		||||
* [Fix] kqueue: watch the directory even if it isn't a new watch (thanks @robfig)
 | 
			
		||||
* [Fix] kqueue: modify after recreation of file
 | 
			
		||||
 | 
			
		||||
## v0.7.3 / 2012-09-27
 | 
			
		||||
 | 
			
		||||
* [Fix] kqueue: watch with an existing folder inside the watched folder (thanks @vmirage)
 | 
			
		||||
* [Fix] kqueue: no longer get duplicate CREATE events
 | 
			
		||||
 | 
			
		||||
## v0.7.2 / 2012-09-01
 | 
			
		||||
 | 
			
		||||
* kqueue: events for created directories
 | 
			
		||||
 | 
			
		||||
## v0.7.1 / 2012-07-14
 | 
			
		||||
 | 
			
		||||
* [Fix] for renaming files
 | 
			
		||||
 | 
			
		||||
## v0.7.0 / 2012-07-02
 | 
			
		||||
 | 
			
		||||
* [Feature] FSNotify flags
 | 
			
		||||
* [Fix] inotify: Added file name back to event path
 | 
			
		||||
 | 
			
		||||
## v0.6.0 / 2012-06-06
 | 
			
		||||
 | 
			
		||||
* kqueue: watch files after directory created (thanks @tmc)
 | 
			
		||||
 | 
			
		||||
## v0.5.1 / 2012-05-22
 | 
			
		||||
 | 
			
		||||
* [Fix] inotify: remove all watches before Close()
 | 
			
		||||
 | 
			
		||||
## v0.5.0 / 2012-05-03
 | 
			
		||||
 | 
			
		||||
* [API] kqueue: return errors during watch instead of sending over channel
 | 
			
		||||
* kqueue: match symlink behavior on Linux
 | 
			
		||||
* inotify: add `DELETE_SELF` (requested by @taralx)
 | 
			
		||||
* [Fix] kqueue: handle EINTR (reported by @robfig)
 | 
			
		||||
* [Doc] Godoc example [#1][] (thanks @davecheney)
 | 
			
		||||
 | 
			
		||||
## v0.4.0 / 2012-03-30
 | 
			
		||||
 | 
			
		||||
* Go 1 released: build with go tool
 | 
			
		||||
* [Feature] Windows support using winfsnotify
 | 
			
		||||
* Windows does not have attribute change notifications
 | 
			
		||||
* Roll attribute notifications into IsModify
 | 
			
		||||
 | 
			
		||||
## v0.3.0 / 2012-02-19
 | 
			
		||||
 | 
			
		||||
* kqueue: add files when watch directory
 | 
			
		||||
 | 
			
		||||
## v0.2.0 / 2011-12-30
 | 
			
		||||
 | 
			
		||||
* update to latest Go weekly code
 | 
			
		||||
 | 
			
		||||
## v0.1.0 / 2011-10-19
 | 
			
		||||
 | 
			
		||||
* kqueue: add watch on file creation to match inotify
 | 
			
		||||
* kqueue: create file event
 | 
			
		||||
* inotify: ignore `IN_IGNORED` events
 | 
			
		||||
* event String()
 | 
			
		||||
* linux: common FileEvent functions
 | 
			
		||||
* initial commit
 | 
			
		||||
 | 
			
		||||
[#79]: https://github.com/howeyc/fsnotify/pull/79
 | 
			
		||||
[#77]: https://github.com/howeyc/fsnotify/pull/77
 | 
			
		||||
[#72]: https://github.com/howeyc/fsnotify/issues/72
 | 
			
		||||
[#71]: https://github.com/howeyc/fsnotify/issues/71
 | 
			
		||||
[#70]: https://github.com/howeyc/fsnotify/issues/70
 | 
			
		||||
[#63]: https://github.com/howeyc/fsnotify/issues/63
 | 
			
		||||
[#62]: https://github.com/howeyc/fsnotify/issues/62
 | 
			
		||||
[#60]: https://github.com/howeyc/fsnotify/issues/60
 | 
			
		||||
[#59]: https://github.com/howeyc/fsnotify/issues/59
 | 
			
		||||
[#49]: https://github.com/howeyc/fsnotify/issues/49
 | 
			
		||||
[#45]: https://github.com/howeyc/fsnotify/issues/45
 | 
			
		||||
[#40]: https://github.com/howeyc/fsnotify/issues/40
 | 
			
		||||
[#36]: https://github.com/howeyc/fsnotify/issues/36
 | 
			
		||||
[#33]: https://github.com/howeyc/fsnotify/issues/33
 | 
			
		||||
[#29]: https://github.com/howeyc/fsnotify/issues/29
 | 
			
		||||
[#25]: https://github.com/howeyc/fsnotify/issues/25
 | 
			
		||||
[#24]: https://github.com/howeyc/fsnotify/issues/24
 | 
			
		||||
[#21]: https://github.com/howeyc/fsnotify/issues/21
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,77 @@
 | 
			
		|||
# Contributing
 | 
			
		||||
 | 
			
		||||
## Issues
 | 
			
		||||
 | 
			
		||||
* Request features and report bugs using the [GitHub Issue Tracker](https://github.com/fsnotify/fsnotify/issues).
 | 
			
		||||
* Please indicate the platform you are using fsnotify on.
 | 
			
		||||
* A code example to reproduce the problem is appreciated.
 | 
			
		||||
 | 
			
		||||
## Pull Requests
 | 
			
		||||
 | 
			
		||||
### Contributor License Agreement
 | 
			
		||||
 | 
			
		||||
fsnotify is derived from code in the [golang.org/x/exp](https://godoc.org/golang.org/x/exp) package and it may be included [in the standard library](https://github.com/fsnotify/fsnotify/issues/1) in the future. Therefore fsnotify carries the same [LICENSE](https://github.com/fsnotify/fsnotify/blob/master/LICENSE) as Go. Contributors retain their copyright, so you need to fill out a short form before we can accept your contribution: [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual).
 | 
			
		||||
 | 
			
		||||
Please indicate that you have signed the CLA in your pull request.
 | 
			
		||||
 | 
			
		||||
### How fsnotify is Developed
 | 
			
		||||
 | 
			
		||||
* Development is done on feature branches.
 | 
			
		||||
* Tests are run on BSD, Linux, macOS and Windows.
 | 
			
		||||
* Pull requests are reviewed and [applied to master][am] using [hub][].
 | 
			
		||||
  * Maintainers may modify or squash commits rather than asking contributors to.
 | 
			
		||||
* To issue a new release, the maintainers will:
 | 
			
		||||
  * Update the CHANGELOG
 | 
			
		||||
  * Tag a version, which will become available through gopkg.in.
 | 
			
		||||
 
 | 
			
		||||
### How to Fork
 | 
			
		||||
 | 
			
		||||
For smooth sailing, always use the original import path. Installing with `go get` makes this easy. 
 | 
			
		||||
 | 
			
		||||
1. Install from GitHub (`go get -u github.com/fsnotify/fsnotify`)
 | 
			
		||||
2. Create your feature branch (`git checkout -b my-new-feature`)
 | 
			
		||||
3. Ensure everything works and the tests pass (see below)
 | 
			
		||||
4. Commit your changes (`git commit -am 'Add some feature'`)
 | 
			
		||||
 | 
			
		||||
Contribute upstream:
 | 
			
		||||
 | 
			
		||||
1. Fork fsnotify on GitHub
 | 
			
		||||
2. Add your remote (`git remote add fork git@github.com:mycompany/repo.git`)
 | 
			
		||||
3. Push to the branch (`git push fork my-new-feature`)
 | 
			
		||||
4. Create a new Pull Request on GitHub
 | 
			
		||||
 | 
			
		||||
This workflow is [thoroughly explained by Katrina Owen](https://splice.com/blog/contributing-open-source-git-repositories-go/).
 | 
			
		||||
 | 
			
		||||
### Testing
 | 
			
		||||
 | 
			
		||||
fsnotify uses build tags to compile different code on Linux, BSD, macOS, and Windows.
 | 
			
		||||
 | 
			
		||||
Before doing a pull request, please do your best to test your changes on multiple platforms, and list which platforms you were able/unable to test on.
 | 
			
		||||
 | 
			
		||||
To aid in cross-platform testing there is a Vagrantfile for Linux and BSD.
 | 
			
		||||
 | 
			
		||||
* Install [Vagrant](http://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/)
 | 
			
		||||
* Setup [Vagrant Gopher](https://github.com/nathany/vagrant-gopher) in your `src` folder.
 | 
			
		||||
* Run `vagrant up` from the project folder. You can also setup just one box with `vagrant up linux` or `vagrant up bsd` (note: the BSD box doesn't support Windows hosts at this time, and NFS may prompt for your host OS password)
 | 
			
		||||
* Once setup, you can run the test suite on a given OS with a single command `vagrant ssh linux -c 'cd fsnotify/fsnotify; go test'`.
 | 
			
		||||
* When you're done, you will want to halt or destroy the Vagrant boxes.
 | 
			
		||||
 | 
			
		||||
Notice: fsnotify file system events won't trigger in shared folders. The tests get around this limitation by using the /tmp directory.
 | 
			
		||||
 | 
			
		||||
Right now there is no equivalent solution for Windows and macOS, but there are Windows VMs [freely available from Microsoft](http://www.modern.ie/en-us/virtualization-tools#downloads).
 | 
			
		||||
 | 
			
		||||
### Maintainers
 | 
			
		||||
 | 
			
		||||
Help maintaining fsnotify is welcome. To be a maintainer:
 | 
			
		||||
 | 
			
		||||
* Submit a pull request and sign the CLA as above.
 | 
			
		||||
* You must be able to run the test suite on Mac, Windows, Linux and BSD.
 | 
			
		||||
 | 
			
		||||
To keep master clean, the fsnotify project uses the "apply mail" workflow outlined in Nathaniel Talbott's post ["Merge pull request" Considered Harmful][am]. This requires installing [hub][].
 | 
			
		||||
 | 
			
		||||
All code changes should be internal pull requests.
 | 
			
		||||
 | 
			
		||||
Releases are tagged using [Semantic Versioning](http://semver.org/).
 | 
			
		||||
 | 
			
		||||
[hub]: https://github.com/github/hub
 | 
			
		||||
[am]: http://blog.spreedly.com/2014/06/24/merge-pull-request-considered-harmful/#.VGa5yZPF_Zs
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
Copyright (c) 2012 The Go Authors. All rights reserved.
 | 
			
		||||
Copyright (c) 2012 fsnotify Authors. All rights reserved.
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are
 | 
			
		||||
met:
 | 
			
		||||
 | 
			
		||||
   * Redistributions of source code must retain the above copyright
 | 
			
		||||
notice, this list of conditions and the following disclaimer.
 | 
			
		||||
   * Redistributions in binary form must reproduce the above
 | 
			
		||||
copyright notice, this list of conditions and the following disclaimer
 | 
			
		||||
in the documentation and/or other materials provided with the
 | 
			
		||||
distribution.
 | 
			
		||||
   * Neither the name of Google Inc. nor the names of its
 | 
			
		||||
contributors may be used to endorse or promote products derived from
 | 
			
		||||
this software without specific prior written permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
			
		||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
			
		||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
			
		||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,79 @@
 | 
			
		|||
# File system notifications for Go
 | 
			
		||||
 | 
			
		||||
[](https://godoc.org/github.com/fsnotify/fsnotify) [](https://goreportcard.com/report/github.com/fsnotify/fsnotify)
 | 
			
		||||
 | 
			
		||||
fsnotify utilizes [golang.org/x/sys](https://godoc.org/golang.org/x/sys) rather than `syscall` from the standard library. Ensure you have the latest version installed by running:
 | 
			
		||||
 | 
			
		||||
```console
 | 
			
		||||
go get -u golang.org/x/sys/...
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Cross platform: Windows, Linux, BSD and macOS.
 | 
			
		||||
 | 
			
		||||
|Adapter   |OS        |Status    |
 | 
			
		||||
|----------|----------|----------|
 | 
			
		||||
|inotify   |Linux 2.6.27 or later, Android\*|Supported [](https://travis-ci.org/fsnotify/fsnotify)|
 | 
			
		||||
|kqueue    |BSD, macOS, iOS\*|Supported [](https://travis-ci.org/fsnotify/fsnotify)|
 | 
			
		||||
|ReadDirectoryChangesW|Windows|Supported [](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)|
 | 
			
		||||
|FSEvents  |macOS         |[Planned](https://github.com/fsnotify/fsnotify/issues/11)|
 | 
			
		||||
|FEN       |Solaris 11    |[In Progress](https://github.com/fsnotify/fsnotify/issues/12)|
 | 
			
		||||
|fanotify  |Linux 2.6.37+ | |
 | 
			
		||||
|USN Journals |Windows    |[Maybe](https://github.com/fsnotify/fsnotify/issues/53)|
 | 
			
		||||
|Polling   |*All*         |[Maybe](https://github.com/fsnotify/fsnotify/issues/9)|
 | 
			
		||||
 | 
			
		||||
\* Android and iOS are untested.
 | 
			
		||||
 | 
			
		||||
Please see [the documentation](https://godoc.org/github.com/fsnotify/fsnotify) and consult the [FAQ](#faq) for usage information.
 | 
			
		||||
 | 
			
		||||
## API stability
 | 
			
		||||
 | 
			
		||||
fsnotify is a fork of [howeyc/fsnotify](https://godoc.org/github.com/howeyc/fsnotify) with a new API as of v1.0. The API is based on [this design document](http://goo.gl/MrYxyA). 
 | 
			
		||||
 | 
			
		||||
All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based on [Semantic Versioning](http://semver.org/). Further API changes are [planned](https://github.com/fsnotify/fsnotify/milestones), and will be tagged with a new major revision number.
 | 
			
		||||
 | 
			
		||||
Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`.
 | 
			
		||||
 | 
			
		||||
## Contributing
 | 
			
		||||
 | 
			
		||||
Please refer to [CONTRIBUTING][] before opening an issue or pull request.
 | 
			
		||||
 | 
			
		||||
## Example
 | 
			
		||||
 | 
			
		||||
See [example_test.go](https://github.com/fsnotify/fsnotify/blob/master/example_test.go).
 | 
			
		||||
 | 
			
		||||
## FAQ
 | 
			
		||||
 | 
			
		||||
**When a file is moved to another directory is it still being watched?**
 | 
			
		||||
 | 
			
		||||
No (it shouldn't be, unless you are watching where it was moved to).
 | 
			
		||||
 | 
			
		||||
**When I watch a directory, are all subdirectories watched as well?**
 | 
			
		||||
 | 
			
		||||
No, you must add watches for any directory you want to watch (a recursive watcher is on the roadmap [#18][]).
 | 
			
		||||
 | 
			
		||||
**Do I have to watch the Error and Event channels in a separate goroutine?**
 | 
			
		||||
 | 
			
		||||
As of now, yes. Looking into making this single-thread friendly (see [howeyc #7][#7])
 | 
			
		||||
 | 
			
		||||
**Why am I receiving multiple events for the same file on OS X?**
 | 
			
		||||
 | 
			
		||||
Spotlight indexing on OS X can result in multiple events (see [howeyc #62][#62]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#11][]).
 | 
			
		||||
 | 
			
		||||
**How many files can be watched at once?**
 | 
			
		||||
 | 
			
		||||
There are OS-specific limits as to how many watches can be created:
 | 
			
		||||
* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error.
 | 
			
		||||
* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error.
 | 
			
		||||
 | 
			
		||||
[#62]: https://github.com/howeyc/fsnotify/issues/62
 | 
			
		||||
[#18]: https://github.com/fsnotify/fsnotify/issues/18
 | 
			
		||||
[#11]: https://github.com/fsnotify/fsnotify/issues/11
 | 
			
		||||
[#7]: https://github.com/howeyc/fsnotify/issues/7
 | 
			
		||||
 | 
			
		||||
[contributing]: https://github.com/fsnotify/fsnotify/blob/master/CONTRIBUTING.md
 | 
			
		||||
 | 
			
		||||
## Related Projects
 | 
			
		||||
 | 
			
		||||
* [notify](https://github.com/rjeczalik/notify)
 | 
			
		||||
* [fsevents](https://github.com/fsnotify/fsevents)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
// Copyright 2010 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build solaris
 | 
			
		||||
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Watcher watches a set of files, delivering events to a channel.
 | 
			
		||||
type Watcher struct {
 | 
			
		||||
	Events chan Event
 | 
			
		||||
	Errors chan error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
 | 
			
		||||
func NewWatcher() (*Watcher, error) {
 | 
			
		||||
	return nil, errors.New("FEN based watcher not yet supported for fsnotify\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close removes all watches and closes the events channel.
 | 
			
		||||
func (w *Watcher) Close() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add starts watching the named file or directory (non-recursively).
 | 
			
		||||
func (w *Watcher) Add(name string) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Remove stops watching the the named file or directory (non-recursively).
 | 
			
		||||
func (w *Watcher) Remove(name string) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,66 @@
 | 
			
		|||
// Copyright 2012 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !plan9
 | 
			
		||||
 | 
			
		||||
// Package fsnotify provides a platform-independent interface for file system notifications.
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Event represents a single file system notification.
 | 
			
		||||
type Event struct {
 | 
			
		||||
	Name string // Relative path to the file or directory.
 | 
			
		||||
	Op   Op     // File operation that triggered the event.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Op describes a set of file operations.
 | 
			
		||||
type Op uint32
 | 
			
		||||
 | 
			
		||||
// These are the generalized file operations that can trigger a notification.
 | 
			
		||||
const (
 | 
			
		||||
	Create Op = 1 << iota
 | 
			
		||||
	Write
 | 
			
		||||
	Remove
 | 
			
		||||
	Rename
 | 
			
		||||
	Chmod
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (op Op) String() string {
 | 
			
		||||
	// Use a buffer for efficient string concatenation
 | 
			
		||||
	var buffer bytes.Buffer
 | 
			
		||||
 | 
			
		||||
	if op&Create == Create {
 | 
			
		||||
		buffer.WriteString("|CREATE")
 | 
			
		||||
	}
 | 
			
		||||
	if op&Remove == Remove {
 | 
			
		||||
		buffer.WriteString("|REMOVE")
 | 
			
		||||
	}
 | 
			
		||||
	if op&Write == Write {
 | 
			
		||||
		buffer.WriteString("|WRITE")
 | 
			
		||||
	}
 | 
			
		||||
	if op&Rename == Rename {
 | 
			
		||||
		buffer.WriteString("|RENAME")
 | 
			
		||||
	}
 | 
			
		||||
	if op&Chmod == Chmod {
 | 
			
		||||
		buffer.WriteString("|CHMOD")
 | 
			
		||||
	}
 | 
			
		||||
	if buffer.Len() == 0 {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	return buffer.String()[1:] // Strip leading pipe
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String returns a string representation of the event in the form
 | 
			
		||||
// "file: REMOVE|WRITE|..."
 | 
			
		||||
func (e Event) String() string {
 | 
			
		||||
	return fmt.Sprintf("%q: %s", e.Name, e.Op.String())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Common errors that can be reported by a watcher
 | 
			
		||||
var ErrEventOverflow = errors.New("fsnotify queue overflow")
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,337 @@
 | 
			
		|||
// Copyright 2010 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build linux
 | 
			
		||||
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/sys/unix"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Watcher watches a set of files, delivering events to a channel.
 | 
			
		||||
type Watcher struct {
 | 
			
		||||
	Events   chan Event
 | 
			
		||||
	Errors   chan error
 | 
			
		||||
	mu       sync.Mutex // Map access
 | 
			
		||||
	fd       int
 | 
			
		||||
	poller   *fdPoller
 | 
			
		||||
	watches  map[string]*watch // Map of inotify watches (key: path)
 | 
			
		||||
	paths    map[int]string    // Map of watched paths (key: watch descriptor)
 | 
			
		||||
	done     chan struct{}     // Channel for sending a "quit message" to the reader goroutine
 | 
			
		||||
	doneResp chan struct{}     // Channel to respond to Close
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
 | 
			
		||||
func NewWatcher() (*Watcher, error) {
 | 
			
		||||
	// Create inotify fd
 | 
			
		||||
	fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC)
 | 
			
		||||
	if fd == -1 {
 | 
			
		||||
		return nil, errno
 | 
			
		||||
	}
 | 
			
		||||
	// Create epoll
 | 
			
		||||
	poller, err := newFdPoller(fd)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		unix.Close(fd)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	w := &Watcher{
 | 
			
		||||
		fd:       fd,
 | 
			
		||||
		poller:   poller,
 | 
			
		||||
		watches:  make(map[string]*watch),
 | 
			
		||||
		paths:    make(map[int]string),
 | 
			
		||||
		Events:   make(chan Event),
 | 
			
		||||
		Errors:   make(chan error),
 | 
			
		||||
		done:     make(chan struct{}),
 | 
			
		||||
		doneResp: make(chan struct{}),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go w.readEvents()
 | 
			
		||||
	return w, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) isClosed() bool {
 | 
			
		||||
	select {
 | 
			
		||||
	case <-w.done:
 | 
			
		||||
		return true
 | 
			
		||||
	default:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close removes all watches and closes the events channel.
 | 
			
		||||
func (w *Watcher) Close() error {
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Send 'close' signal to goroutine, and set the Watcher to closed.
 | 
			
		||||
	close(w.done)
 | 
			
		||||
 | 
			
		||||
	// Wake up goroutine
 | 
			
		||||
	w.poller.wake()
 | 
			
		||||
 | 
			
		||||
	// Wait for goroutine to close
 | 
			
		||||
	<-w.doneResp
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add starts watching the named file or directory (non-recursively).
 | 
			
		||||
func (w *Watcher) Add(name string) error {
 | 
			
		||||
	name = filepath.Clean(name)
 | 
			
		||||
	if w.isClosed() {
 | 
			
		||||
		return errors.New("inotify instance already closed")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const agnosticEvents = unix.IN_MOVED_TO | unix.IN_MOVED_FROM |
 | 
			
		||||
		unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY |
 | 
			
		||||
		unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF
 | 
			
		||||
 | 
			
		||||
	var flags uint32 = agnosticEvents
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
	watchEntry := w.watches[name]
 | 
			
		||||
	if watchEntry != nil {
 | 
			
		||||
		flags |= watchEntry.flags | unix.IN_MASK_ADD
 | 
			
		||||
	}
 | 
			
		||||
	wd, errno := unix.InotifyAddWatch(w.fd, name, flags)
 | 
			
		||||
	if wd == -1 {
 | 
			
		||||
		return errno
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if watchEntry == nil {
 | 
			
		||||
		w.watches[name] = &watch{wd: uint32(wd), flags: flags}
 | 
			
		||||
		w.paths[wd] = name
 | 
			
		||||
	} else {
 | 
			
		||||
		watchEntry.wd = uint32(wd)
 | 
			
		||||
		watchEntry.flags = flags
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Remove stops watching the named file or directory (non-recursively).
 | 
			
		||||
func (w *Watcher) Remove(name string) error {
 | 
			
		||||
	name = filepath.Clean(name)
 | 
			
		||||
 | 
			
		||||
	// Fetch the watch.
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	defer w.mu.Unlock()
 | 
			
		||||
	watch, ok := w.watches[name]
 | 
			
		||||
 | 
			
		||||
	// Remove it from inotify.
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return fmt.Errorf("can't remove non-existent inotify watch for: %s", name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We successfully removed the watch if InotifyRmWatch doesn't return an
 | 
			
		||||
	// error, we need to clean up our internal state to ensure it matches
 | 
			
		||||
	// inotify's kernel state.
 | 
			
		||||
	delete(w.paths, int(watch.wd))
 | 
			
		||||
	delete(w.watches, name)
 | 
			
		||||
 | 
			
		||||
	// inotify_rm_watch will return EINVAL if the file has been deleted;
 | 
			
		||||
	// the inotify will already have been removed.
 | 
			
		||||
	// watches and pathes are deleted in ignoreLinux() implicitly and asynchronously
 | 
			
		||||
	// by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE
 | 
			
		||||
	// so that EINVAL means that the wd is being rm_watch()ed or its file removed
 | 
			
		||||
	// by another thread and we have not received IN_IGNORE event.
 | 
			
		||||
	success, errno := unix.InotifyRmWatch(w.fd, watch.wd)
 | 
			
		||||
	if success == -1 {
 | 
			
		||||
		// TODO: Perhaps it's not helpful to return an error here in every case.
 | 
			
		||||
		// the only two possible errors are:
 | 
			
		||||
		// EBADF, which happens when w.fd is not a valid file descriptor of any kind.
 | 
			
		||||
		// EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor.
 | 
			
		||||
		// Watch descriptors are invalidated when they are removed explicitly or implicitly;
 | 
			
		||||
		// explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted.
 | 
			
		||||
		return errno
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type watch struct {
 | 
			
		||||
	wd    uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
 | 
			
		||||
	flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readEvents reads from the inotify file descriptor, converts the
 | 
			
		||||
// received events into Event objects and sends them via the Events channel
 | 
			
		||||
func (w *Watcher) readEvents() {
 | 
			
		||||
	var (
 | 
			
		||||
		buf   [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
 | 
			
		||||
		n     int                                  // Number of bytes read with read()
 | 
			
		||||
		errno error                                // Syscall errno
 | 
			
		||||
		ok    bool                                 // For poller.wait
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	defer close(w.doneResp)
 | 
			
		||||
	defer close(w.Errors)
 | 
			
		||||
	defer close(w.Events)
 | 
			
		||||
	defer unix.Close(w.fd)
 | 
			
		||||
	defer w.poller.close()
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		// See if we have been closed.
 | 
			
		||||
		if w.isClosed() {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ok, errno = w.poller.wait()
 | 
			
		||||
		if errno != nil {
 | 
			
		||||
			select {
 | 
			
		||||
			case w.Errors <- errno:
 | 
			
		||||
			case <-w.done:
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !ok {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		n, errno = unix.Read(w.fd, buf[:])
 | 
			
		||||
		// If a signal interrupted execution, see if we've been asked to close, and try again.
 | 
			
		||||
		// http://man7.org/linux/man-pages/man7/signal.7.html :
 | 
			
		||||
		// "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable"
 | 
			
		||||
		if errno == unix.EINTR {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// unix.Read might have been woken up by Close. If so, we're done.
 | 
			
		||||
		if w.isClosed() {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if n < unix.SizeofInotifyEvent {
 | 
			
		||||
			var err error
 | 
			
		||||
			if n == 0 {
 | 
			
		||||
				// If EOF is received. This should really never happen.
 | 
			
		||||
				err = io.EOF
 | 
			
		||||
			} else if n < 0 {
 | 
			
		||||
				// If an error occurred while reading.
 | 
			
		||||
				err = errno
 | 
			
		||||
			} else {
 | 
			
		||||
				// Read was too short.
 | 
			
		||||
				err = errors.New("notify: short read in readEvents()")
 | 
			
		||||
			}
 | 
			
		||||
			select {
 | 
			
		||||
			case w.Errors <- err:
 | 
			
		||||
			case <-w.done:
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var offset uint32
 | 
			
		||||
		// We don't know how many events we just read into the buffer
 | 
			
		||||
		// While the offset points to at least one whole event...
 | 
			
		||||
		for offset <= uint32(n-unix.SizeofInotifyEvent) {
 | 
			
		||||
			// Point "raw" to the event in the buffer
 | 
			
		||||
			raw := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset]))
 | 
			
		||||
 | 
			
		||||
			mask := uint32(raw.Mask)
 | 
			
		||||
			nameLen := uint32(raw.Len)
 | 
			
		||||
 | 
			
		||||
			if mask&unix.IN_Q_OVERFLOW != 0 {
 | 
			
		||||
				select {
 | 
			
		||||
				case w.Errors <- ErrEventOverflow:
 | 
			
		||||
				case <-w.done:
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// If the event happened to the watched directory or the watched file, the kernel
 | 
			
		||||
			// doesn't append the filename to the event, but we would like to always fill the
 | 
			
		||||
			// the "Name" field with a valid filename. We retrieve the path of the watch from
 | 
			
		||||
			// the "paths" map.
 | 
			
		||||
			w.mu.Lock()
 | 
			
		||||
			name, ok := w.paths[int(raw.Wd)]
 | 
			
		||||
			// IN_DELETE_SELF occurs when the file/directory being watched is removed.
 | 
			
		||||
			// This is a sign to clean up the maps, otherwise we are no longer in sync
 | 
			
		||||
			// with the inotify kernel state which has already deleted the watch
 | 
			
		||||
			// automatically.
 | 
			
		||||
			if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
 | 
			
		||||
				delete(w.paths, int(raw.Wd))
 | 
			
		||||
				delete(w.watches, name)
 | 
			
		||||
			}
 | 
			
		||||
			w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
			if nameLen > 0 {
 | 
			
		||||
				// Point "bytes" at the first byte of the filename
 | 
			
		||||
				bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))
 | 
			
		||||
				// The filename is padded with NULL bytes. TrimRight() gets rid of those.
 | 
			
		||||
				name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			event := newEvent(name, mask)
 | 
			
		||||
 | 
			
		||||
			// Send the events that are not ignored on the events channel
 | 
			
		||||
			if !event.ignoreLinux(mask) {
 | 
			
		||||
				select {
 | 
			
		||||
				case w.Events <- event:
 | 
			
		||||
				case <-w.done:
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Move to the next event in the buffer
 | 
			
		||||
			offset += unix.SizeofInotifyEvent + nameLen
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Certain types of events can be "ignored" and not sent over the Events
 | 
			
		||||
// channel. Such as events marked ignore by the kernel, or MODIFY events
 | 
			
		||||
// against files that do not exist.
 | 
			
		||||
func (e *Event) ignoreLinux(mask uint32) bool {
 | 
			
		||||
	// Ignore anything the inotify API says to ignore
 | 
			
		||||
	if mask&unix.IN_IGNORED == unix.IN_IGNORED {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If the event is not a DELETE or RENAME, the file must exist.
 | 
			
		||||
	// Otherwise the event is ignored.
 | 
			
		||||
	// *Note*: this was put in place because it was seen that a MODIFY
 | 
			
		||||
	// event was sent after the DELETE. This ignores that MODIFY and
 | 
			
		||||
	// assumes a DELETE will come or has come if the file doesn't exist.
 | 
			
		||||
	if !(e.Op&Remove == Remove || e.Op&Rename == Rename) {
 | 
			
		||||
		_, statErr := os.Lstat(e.Name)
 | 
			
		||||
		return os.IsNotExist(statErr)
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newEvent returns an platform-independent Event based on an inotify mask.
 | 
			
		||||
func newEvent(name string, mask uint32) Event {
 | 
			
		||||
	e := Event{Name: name}
 | 
			
		||||
	if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO {
 | 
			
		||||
		e.Op |= Create
 | 
			
		||||
	}
 | 
			
		||||
	if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE {
 | 
			
		||||
		e.Op |= Remove
 | 
			
		||||
	}
 | 
			
		||||
	if mask&unix.IN_MODIFY == unix.IN_MODIFY {
 | 
			
		||||
		e.Op |= Write
 | 
			
		||||
	}
 | 
			
		||||
	if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM {
 | 
			
		||||
		e.Op |= Rename
 | 
			
		||||
	}
 | 
			
		||||
	if mask&unix.IN_ATTRIB == unix.IN_ATTRIB {
 | 
			
		||||
		e.Op |= Chmod
 | 
			
		||||
	}
 | 
			
		||||
	return e
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,187 @@
 | 
			
		|||
// Copyright 2015 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build linux
 | 
			
		||||
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/sys/unix"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type fdPoller struct {
 | 
			
		||||
	fd   int    // File descriptor (as returned by the inotify_init() syscall)
 | 
			
		||||
	epfd int    // Epoll file descriptor
 | 
			
		||||
	pipe [2]int // Pipe for waking up
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func emptyPoller(fd int) *fdPoller {
 | 
			
		||||
	poller := new(fdPoller)
 | 
			
		||||
	poller.fd = fd
 | 
			
		||||
	poller.epfd = -1
 | 
			
		||||
	poller.pipe[0] = -1
 | 
			
		||||
	poller.pipe[1] = -1
 | 
			
		||||
	return poller
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create a new inotify poller.
 | 
			
		||||
// This creates an inotify handler, and an epoll handler.
 | 
			
		||||
func newFdPoller(fd int) (*fdPoller, error) {
 | 
			
		||||
	var errno error
 | 
			
		||||
	poller := emptyPoller(fd)
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if errno != nil {
 | 
			
		||||
			poller.close()
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	poller.fd = fd
 | 
			
		||||
 | 
			
		||||
	// Create epoll fd
 | 
			
		||||
	poller.epfd, errno = unix.EpollCreate1(0)
 | 
			
		||||
	if poller.epfd == -1 {
 | 
			
		||||
		return nil, errno
 | 
			
		||||
	}
 | 
			
		||||
	// Create pipe; pipe[0] is the read end, pipe[1] the write end.
 | 
			
		||||
	errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK)
 | 
			
		||||
	if errno != nil {
 | 
			
		||||
		return nil, errno
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Register inotify fd with epoll
 | 
			
		||||
	event := unix.EpollEvent{
 | 
			
		||||
		Fd:     int32(poller.fd),
 | 
			
		||||
		Events: unix.EPOLLIN,
 | 
			
		||||
	}
 | 
			
		||||
	errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.fd, &event)
 | 
			
		||||
	if errno != nil {
 | 
			
		||||
		return nil, errno
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Register pipe fd with epoll
 | 
			
		||||
	event = unix.EpollEvent{
 | 
			
		||||
		Fd:     int32(poller.pipe[0]),
 | 
			
		||||
		Events: unix.EPOLLIN,
 | 
			
		||||
	}
 | 
			
		||||
	errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.pipe[0], &event)
 | 
			
		||||
	if errno != nil {
 | 
			
		||||
		return nil, errno
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return poller, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Wait using epoll.
 | 
			
		||||
// Returns true if something is ready to be read,
 | 
			
		||||
// false if there is not.
 | 
			
		||||
func (poller *fdPoller) wait() (bool, error) {
 | 
			
		||||
	// 3 possible events per fd, and 2 fds, makes a maximum of 6 events.
 | 
			
		||||
	// I don't know whether epoll_wait returns the number of events returned,
 | 
			
		||||
	// or the total number of events ready.
 | 
			
		||||
	// I decided to catch both by making the buffer one larger than the maximum.
 | 
			
		||||
	events := make([]unix.EpollEvent, 7)
 | 
			
		||||
	for {
 | 
			
		||||
		n, errno := unix.EpollWait(poller.epfd, events, -1)
 | 
			
		||||
		if n == -1 {
 | 
			
		||||
			if errno == unix.EINTR {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			return false, errno
 | 
			
		||||
		}
 | 
			
		||||
		if n == 0 {
 | 
			
		||||
			// If there are no events, try again.
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if n > 6 {
 | 
			
		||||
			// This should never happen. More events were returned than should be possible.
 | 
			
		||||
			return false, errors.New("epoll_wait returned more events than I know what to do with")
 | 
			
		||||
		}
 | 
			
		||||
		ready := events[:n]
 | 
			
		||||
		epollhup := false
 | 
			
		||||
		epollerr := false
 | 
			
		||||
		epollin := false
 | 
			
		||||
		for _, event := range ready {
 | 
			
		||||
			if event.Fd == int32(poller.fd) {
 | 
			
		||||
				if event.Events&unix.EPOLLHUP != 0 {
 | 
			
		||||
					// This should not happen, but if it does, treat it as a wakeup.
 | 
			
		||||
					epollhup = true
 | 
			
		||||
				}
 | 
			
		||||
				if event.Events&unix.EPOLLERR != 0 {
 | 
			
		||||
					// If an error is waiting on the file descriptor, we should pretend
 | 
			
		||||
					// something is ready to read, and let unix.Read pick up the error.
 | 
			
		||||
					epollerr = true
 | 
			
		||||
				}
 | 
			
		||||
				if event.Events&unix.EPOLLIN != 0 {
 | 
			
		||||
					// There is data to read.
 | 
			
		||||
					epollin = true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if event.Fd == int32(poller.pipe[0]) {
 | 
			
		||||
				if event.Events&unix.EPOLLHUP != 0 {
 | 
			
		||||
					// Write pipe descriptor was closed, by us. This means we're closing down the
 | 
			
		||||
					// watcher, and we should wake up.
 | 
			
		||||
				}
 | 
			
		||||
				if event.Events&unix.EPOLLERR != 0 {
 | 
			
		||||
					// If an error is waiting on the pipe file descriptor.
 | 
			
		||||
					// This is an absolute mystery, and should never ever happen.
 | 
			
		||||
					return false, errors.New("Error on the pipe descriptor.")
 | 
			
		||||
				}
 | 
			
		||||
				if event.Events&unix.EPOLLIN != 0 {
 | 
			
		||||
					// This is a regular wakeup, so we have to clear the buffer.
 | 
			
		||||
					err := poller.clearWake()
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return false, err
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if epollhup || epollerr || epollin {
 | 
			
		||||
			return true, nil
 | 
			
		||||
		}
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close the write end of the poller.
 | 
			
		||||
func (poller *fdPoller) wake() error {
 | 
			
		||||
	buf := make([]byte, 1)
 | 
			
		||||
	n, errno := unix.Write(poller.pipe[1], buf)
 | 
			
		||||
	if n == -1 {
 | 
			
		||||
		if errno == unix.EAGAIN {
 | 
			
		||||
			// Buffer is full, poller will wake.
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		return errno
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (poller *fdPoller) clearWake() error {
 | 
			
		||||
	// You have to be woken up a LOT in order to get to 100!
 | 
			
		||||
	buf := make([]byte, 100)
 | 
			
		||||
	n, errno := unix.Read(poller.pipe[0], buf)
 | 
			
		||||
	if n == -1 {
 | 
			
		||||
		if errno == unix.EAGAIN {
 | 
			
		||||
			// Buffer is empty, someone else cleared our wake.
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		return errno
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close all poller file descriptors, but not the one passed to it.
 | 
			
		||||
func (poller *fdPoller) close() {
 | 
			
		||||
	if poller.pipe[1] != -1 {
 | 
			
		||||
		unix.Close(poller.pipe[1])
 | 
			
		||||
	}
 | 
			
		||||
	if poller.pipe[0] != -1 {
 | 
			
		||||
		unix.Close(poller.pipe[0])
 | 
			
		||||
	}
 | 
			
		||||
	if poller.epfd != -1 {
 | 
			
		||||
		unix.Close(poller.epfd)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,503 @@
 | 
			
		|||
// Copyright 2010 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build freebsd openbsd netbsd dragonfly darwin
 | 
			
		||||
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/sys/unix"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Watcher watches a set of files, delivering events to a channel.
 | 
			
		||||
type Watcher struct {
 | 
			
		||||
	Events chan Event
 | 
			
		||||
	Errors chan error
 | 
			
		||||
	done   chan bool // Channel for sending a "quit message" to the reader goroutine
 | 
			
		||||
 | 
			
		||||
	kq int // File descriptor (as returned by the kqueue() syscall).
 | 
			
		||||
 | 
			
		||||
	mu              sync.Mutex        // Protects access to watcher data
 | 
			
		||||
	watches         map[string]int    // Map of watched file descriptors (key: path).
 | 
			
		||||
	externalWatches map[string]bool   // Map of watches added by user of the library.
 | 
			
		||||
	dirFlags        map[string]uint32 // Map of watched directories to fflags used in kqueue.
 | 
			
		||||
	paths           map[int]pathInfo  // Map file descriptors to path names for processing kqueue events.
 | 
			
		||||
	fileExists      map[string]bool   // Keep track of if we know this file exists (to stop duplicate create events).
 | 
			
		||||
	isClosed        bool              // Set to true when Close() is first called
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type pathInfo struct {
 | 
			
		||||
	name  string
 | 
			
		||||
	isDir bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
 | 
			
		||||
func NewWatcher() (*Watcher, error) {
 | 
			
		||||
	kq, err := kqueue()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w := &Watcher{
 | 
			
		||||
		kq:              kq,
 | 
			
		||||
		watches:         make(map[string]int),
 | 
			
		||||
		dirFlags:        make(map[string]uint32),
 | 
			
		||||
		paths:           make(map[int]pathInfo),
 | 
			
		||||
		fileExists:      make(map[string]bool),
 | 
			
		||||
		externalWatches: make(map[string]bool),
 | 
			
		||||
		Events:          make(chan Event),
 | 
			
		||||
		Errors:          make(chan error),
 | 
			
		||||
		done:            make(chan bool),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go w.readEvents()
 | 
			
		||||
	return w, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close removes all watches and closes the events channel.
 | 
			
		||||
func (w *Watcher) Close() error {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	if w.isClosed {
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	w.isClosed = true
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	// copy paths to remove while locked
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	var pathsToRemove = make([]string, 0, len(w.watches))
 | 
			
		||||
	for name := range w.watches {
 | 
			
		||||
		pathsToRemove = append(pathsToRemove, name)
 | 
			
		||||
	}
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
	// unlock before calling Remove, which also locks
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	for _, name := range pathsToRemove {
 | 
			
		||||
		if e := w.Remove(name); e != nil && err == nil {
 | 
			
		||||
			err = e
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Send "quit" message to the reader goroutine:
 | 
			
		||||
	w.done <- true
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add starts watching the named file or directory (non-recursively).
 | 
			
		||||
func (w *Watcher) Add(name string) error {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	w.externalWatches[name] = true
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
	_, err := w.addWatch(name, noteAllEvents)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Remove stops watching the the named file or directory (non-recursively).
 | 
			
		||||
func (w *Watcher) Remove(name string) error {
 | 
			
		||||
	name = filepath.Clean(name)
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	watchfd, ok := w.watches[name]
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return fmt.Errorf("can't remove non-existent kevent watch for: %s", name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const registerRemove = unix.EV_DELETE
 | 
			
		||||
	if err := register(w.kq, []int{watchfd}, registerRemove, 0); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	unix.Close(watchfd)
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	isDir := w.paths[watchfd].isDir
 | 
			
		||||
	delete(w.watches, name)
 | 
			
		||||
	delete(w.paths, watchfd)
 | 
			
		||||
	delete(w.dirFlags, name)
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	// Find all watched paths that are in this directory that are not external.
 | 
			
		||||
	if isDir {
 | 
			
		||||
		var pathsToRemove []string
 | 
			
		||||
		w.mu.Lock()
 | 
			
		||||
		for _, path := range w.paths {
 | 
			
		||||
			wdir, _ := filepath.Split(path.name)
 | 
			
		||||
			if filepath.Clean(wdir) == name {
 | 
			
		||||
				if !w.externalWatches[path.name] {
 | 
			
		||||
					pathsToRemove = append(pathsToRemove, path.name)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
		for _, name := range pathsToRemove {
 | 
			
		||||
			// Since these are internal, not much sense in propagating error
 | 
			
		||||
			// to the user, as that will just confuse them with an error about
 | 
			
		||||
			// a path they did not explicitly watch themselves.
 | 
			
		||||
			w.Remove(name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE)
 | 
			
		||||
const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME
 | 
			
		||||
 | 
			
		||||
// keventWaitTime to block on each read from kevent
 | 
			
		||||
var keventWaitTime = durationToTimespec(100 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
// addWatch adds name to the watched file set.
 | 
			
		||||
// The flags are interpreted as described in kevent(2).
 | 
			
		||||
// Returns the real path to the file which was added, if any, which may be different from the one passed in the case of symlinks.
 | 
			
		||||
func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
 | 
			
		||||
	var isDir bool
 | 
			
		||||
	// Make ./name and name equivalent
 | 
			
		||||
	name = filepath.Clean(name)
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	if w.isClosed {
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
		return "", errors.New("kevent instance already closed")
 | 
			
		||||
	}
 | 
			
		||||
	watchfd, alreadyWatching := w.watches[name]
 | 
			
		||||
	// We already have a watch, but we can still override flags.
 | 
			
		||||
	if alreadyWatching {
 | 
			
		||||
		isDir = w.paths[watchfd].isDir
 | 
			
		||||
	}
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	if !alreadyWatching {
 | 
			
		||||
		fi, err := os.Lstat(name)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Don't watch sockets.
 | 
			
		||||
		if fi.Mode()&os.ModeSocket == os.ModeSocket {
 | 
			
		||||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Don't watch named pipes.
 | 
			
		||||
		if fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe {
 | 
			
		||||
			return "", nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Follow Symlinks
 | 
			
		||||
		// Unfortunately, Linux can add bogus symlinks to watch list without
 | 
			
		||||
		// issue, and Windows can't do symlinks period (AFAIK). To  maintain
 | 
			
		||||
		// consistency, we will act like everything is fine. There will simply
 | 
			
		||||
		// be no file events for broken symlinks.
 | 
			
		||||
		// Hence the returns of nil on errors.
 | 
			
		||||
		if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
 | 
			
		||||
			name, err = filepath.EvalSymlinks(name)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return "", nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			w.mu.Lock()
 | 
			
		||||
			_, alreadyWatching = w.watches[name]
 | 
			
		||||
			w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
			if alreadyWatching {
 | 
			
		||||
				return name, nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			fi, err = os.Lstat(name)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return "", nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		watchfd, err = unix.Open(name, openMode, 0700)
 | 
			
		||||
		if watchfd == -1 {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		isDir = fi.IsDir()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const registerAdd = unix.EV_ADD | unix.EV_CLEAR | unix.EV_ENABLE
 | 
			
		||||
	if err := register(w.kq, []int{watchfd}, registerAdd, flags); err != nil {
 | 
			
		||||
		unix.Close(watchfd)
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !alreadyWatching {
 | 
			
		||||
		w.mu.Lock()
 | 
			
		||||
		w.watches[name] = watchfd
 | 
			
		||||
		w.paths[watchfd] = pathInfo{name: name, isDir: isDir}
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if isDir {
 | 
			
		||||
		// Watch the directory if it has not been watched before,
 | 
			
		||||
		// or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles)
 | 
			
		||||
		w.mu.Lock()
 | 
			
		||||
 | 
			
		||||
		watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE &&
 | 
			
		||||
			(!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE)
 | 
			
		||||
		// Store flags so this watch can be updated later
 | 
			
		||||
		w.dirFlags[name] = flags
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
		if watchDir {
 | 
			
		||||
			if err := w.watchDirectoryFiles(name); err != nil {
 | 
			
		||||
				return "", err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return name, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readEvents reads from kqueue and converts the received kevents into
 | 
			
		||||
// Event values that it sends down the Events channel.
 | 
			
		||||
func (w *Watcher) readEvents() {
 | 
			
		||||
	eventBuffer := make([]unix.Kevent_t, 10)
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		// See if there is a message on the "done" channel
 | 
			
		||||
		select {
 | 
			
		||||
		case <-w.done:
 | 
			
		||||
			err := unix.Close(w.kq)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				w.Errors <- err
 | 
			
		||||
			}
 | 
			
		||||
			close(w.Events)
 | 
			
		||||
			close(w.Errors)
 | 
			
		||||
			return
 | 
			
		||||
		default:
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Get new events
 | 
			
		||||
		kevents, err := read(w.kq, eventBuffer, &keventWaitTime)
 | 
			
		||||
		// EINTR is okay, the syscall was interrupted before timeout expired.
 | 
			
		||||
		if err != nil && err != unix.EINTR {
 | 
			
		||||
			w.Errors <- err
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Flush the events we received to the Events channel
 | 
			
		||||
		for len(kevents) > 0 {
 | 
			
		||||
			kevent := &kevents[0]
 | 
			
		||||
			watchfd := int(kevent.Ident)
 | 
			
		||||
			mask := uint32(kevent.Fflags)
 | 
			
		||||
			w.mu.Lock()
 | 
			
		||||
			path := w.paths[watchfd]
 | 
			
		||||
			w.mu.Unlock()
 | 
			
		||||
			event := newEvent(path.name, mask)
 | 
			
		||||
 | 
			
		||||
			if path.isDir && !(event.Op&Remove == Remove) {
 | 
			
		||||
				// Double check to make sure the directory exists. This can happen when
 | 
			
		||||
				// we do a rm -fr on a recursively watched folders and we receive a
 | 
			
		||||
				// modification event first but the folder has been deleted and later
 | 
			
		||||
				// receive the delete event
 | 
			
		||||
				if _, err := os.Lstat(event.Name); os.IsNotExist(err) {
 | 
			
		||||
					// mark is as delete event
 | 
			
		||||
					event.Op |= Remove
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if event.Op&Rename == Rename || event.Op&Remove == Remove {
 | 
			
		||||
				w.Remove(event.Name)
 | 
			
		||||
				w.mu.Lock()
 | 
			
		||||
				delete(w.fileExists, event.Name)
 | 
			
		||||
				w.mu.Unlock()
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if path.isDir && event.Op&Write == Write && !(event.Op&Remove == Remove) {
 | 
			
		||||
				w.sendDirectoryChangeEvents(event.Name)
 | 
			
		||||
			} else {
 | 
			
		||||
				// Send the event on the Events channel
 | 
			
		||||
				w.Events <- event
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if event.Op&Remove == Remove {
 | 
			
		||||
				// Look for a file that may have overwritten this.
 | 
			
		||||
				// For example, mv f1 f2 will delete f2, then create f2.
 | 
			
		||||
				if path.isDir {
 | 
			
		||||
					fileDir := filepath.Clean(event.Name)
 | 
			
		||||
					w.mu.Lock()
 | 
			
		||||
					_, found := w.watches[fileDir]
 | 
			
		||||
					w.mu.Unlock()
 | 
			
		||||
					if found {
 | 
			
		||||
						// make sure the directory exists before we watch for changes. When we
 | 
			
		||||
						// do a recursive watch and perform rm -fr, the parent directory might
 | 
			
		||||
						// have gone missing, ignore the missing directory and let the
 | 
			
		||||
						// upcoming delete event remove the watch from the parent directory.
 | 
			
		||||
						if _, err := os.Lstat(fileDir); err == nil {
 | 
			
		||||
							w.sendDirectoryChangeEvents(fileDir)
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					filePath := filepath.Clean(event.Name)
 | 
			
		||||
					if fileInfo, err := os.Lstat(filePath); err == nil {
 | 
			
		||||
						w.sendFileCreatedEventIfNew(filePath, fileInfo)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Move to next event
 | 
			
		||||
			kevents = kevents[1:]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newEvent returns an platform-independent Event based on kqueue Fflags.
 | 
			
		||||
func newEvent(name string, mask uint32) Event {
 | 
			
		||||
	e := Event{Name: name}
 | 
			
		||||
	if mask&unix.NOTE_DELETE == unix.NOTE_DELETE {
 | 
			
		||||
		e.Op |= Remove
 | 
			
		||||
	}
 | 
			
		||||
	if mask&unix.NOTE_WRITE == unix.NOTE_WRITE {
 | 
			
		||||
		e.Op |= Write
 | 
			
		||||
	}
 | 
			
		||||
	if mask&unix.NOTE_RENAME == unix.NOTE_RENAME {
 | 
			
		||||
		e.Op |= Rename
 | 
			
		||||
	}
 | 
			
		||||
	if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB {
 | 
			
		||||
		e.Op |= Chmod
 | 
			
		||||
	}
 | 
			
		||||
	return e
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newCreateEvent(name string) Event {
 | 
			
		||||
	return Event{Name: name, Op: Create}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// watchDirectoryFiles to mimic inotify when adding a watch on a directory
 | 
			
		||||
func (w *Watcher) watchDirectoryFiles(dirPath string) error {
 | 
			
		||||
	// Get all files
 | 
			
		||||
	files, err := ioutil.ReadDir(dirPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, fileInfo := range files {
 | 
			
		||||
		filePath := filepath.Join(dirPath, fileInfo.Name())
 | 
			
		||||
		filePath, err = w.internalWatch(filePath, fileInfo)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		w.mu.Lock()
 | 
			
		||||
		w.fileExists[filePath] = true
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sendDirectoryEvents searches the directory for newly created files
 | 
			
		||||
// and sends them over the event channel. This functionality is to have
 | 
			
		||||
// the BSD version of fsnotify match Linux inotify which provides a
 | 
			
		||||
// create event for files created in a watched directory.
 | 
			
		||||
func (w *Watcher) sendDirectoryChangeEvents(dirPath string) {
 | 
			
		||||
	// Get all files
 | 
			
		||||
	files, err := ioutil.ReadDir(dirPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		w.Errors <- err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Search for new files
 | 
			
		||||
	for _, fileInfo := range files {
 | 
			
		||||
		filePath := filepath.Join(dirPath, fileInfo.Name())
 | 
			
		||||
		err := w.sendFileCreatedEventIfNew(filePath, fileInfo)
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sendFileCreatedEvent sends a create event if the file isn't already being tracked.
 | 
			
		||||
func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) {
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	_, doesExist := w.fileExists[filePath]
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
	if !doesExist {
 | 
			
		||||
		// Send create event
 | 
			
		||||
		w.Events <- newCreateEvent(filePath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// like watchDirectoryFiles (but without doing another ReadDir)
 | 
			
		||||
	filePath, err = w.internalWatch(filePath, fileInfo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	w.fileExists[filePath] = true
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) {
 | 
			
		||||
	if fileInfo.IsDir() {
 | 
			
		||||
		// mimic Linux providing delete events for subdirectories
 | 
			
		||||
		// but preserve the flags used if currently watching subdirectory
 | 
			
		||||
		w.mu.Lock()
 | 
			
		||||
		flags := w.dirFlags[name]
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
		flags |= unix.NOTE_DELETE | unix.NOTE_RENAME
 | 
			
		||||
		return w.addWatch(name, flags)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// watch file to mimic Linux inotify
 | 
			
		||||
	return w.addWatch(name, noteAllEvents)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// kqueue creates a new kernel event queue and returns a descriptor.
 | 
			
		||||
func kqueue() (kq int, err error) {
 | 
			
		||||
	kq, err = unix.Kqueue()
 | 
			
		||||
	if kq == -1 {
 | 
			
		||||
		return kq, err
 | 
			
		||||
	}
 | 
			
		||||
	return kq, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// register events with the queue
 | 
			
		||||
func register(kq int, fds []int, flags int, fflags uint32) error {
 | 
			
		||||
	changes := make([]unix.Kevent_t, len(fds))
 | 
			
		||||
 | 
			
		||||
	for i, fd := range fds {
 | 
			
		||||
		// SetKevent converts int to the platform-specific types:
 | 
			
		||||
		unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags)
 | 
			
		||||
		changes[i].Fflags = fflags
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// register the events
 | 
			
		||||
	success, err := unix.Kevent(kq, changes, nil, nil)
 | 
			
		||||
	if success == -1 {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// read retrieves pending events, or waits until an event occurs.
 | 
			
		||||
// A timeout of nil blocks indefinitely, while 0 polls the queue.
 | 
			
		||||
func read(kq int, events []unix.Kevent_t, timeout *unix.Timespec) ([]unix.Kevent_t, error) {
 | 
			
		||||
	n, err := unix.Kevent(kq, nil, events, timeout)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return events[0:n], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// durationToTimespec prepares a timeout value
 | 
			
		||||
func durationToTimespec(d time.Duration) unix.Timespec {
 | 
			
		||||
	return unix.NsecToTimespec(d.Nanoseconds())
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
// Copyright 2013 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build freebsd openbsd netbsd dragonfly
 | 
			
		||||
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
import "golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
const openMode = unix.O_NONBLOCK | unix.O_RDONLY
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
// Copyright 2013 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build darwin
 | 
			
		||||
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
import "golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
// note: this constant is not defined on BSD
 | 
			
		||||
const openMode = unix.O_EVTONLY
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,561 @@
 | 
			
		|||
// Copyright 2011 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build windows
 | 
			
		||||
 | 
			
		||||
package fsnotify
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Watcher watches a set of files, delivering events to a channel.
 | 
			
		||||
type Watcher struct {
 | 
			
		||||
	Events   chan Event
 | 
			
		||||
	Errors   chan error
 | 
			
		||||
	isClosed bool           // Set to true when Close() is first called
 | 
			
		||||
	mu       sync.Mutex     // Map access
 | 
			
		||||
	port     syscall.Handle // Handle to completion port
 | 
			
		||||
	watches  watchMap       // Map of watches (key: i-number)
 | 
			
		||||
	input    chan *input    // Inputs to the reader are sent on this channel
 | 
			
		||||
	quit     chan chan<- error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
 | 
			
		||||
func NewWatcher() (*Watcher, error) {
 | 
			
		||||
	port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
 | 
			
		||||
	if e != nil {
 | 
			
		||||
		return nil, os.NewSyscallError("CreateIoCompletionPort", e)
 | 
			
		||||
	}
 | 
			
		||||
	w := &Watcher{
 | 
			
		||||
		port:    port,
 | 
			
		||||
		watches: make(watchMap),
 | 
			
		||||
		input:   make(chan *input, 1),
 | 
			
		||||
		Events:  make(chan Event, 50),
 | 
			
		||||
		Errors:  make(chan error),
 | 
			
		||||
		quit:    make(chan chan<- error, 1),
 | 
			
		||||
	}
 | 
			
		||||
	go w.readEvents()
 | 
			
		||||
	return w, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close removes all watches and closes the events channel.
 | 
			
		||||
func (w *Watcher) Close() error {
 | 
			
		||||
	if w.isClosed {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	w.isClosed = true
 | 
			
		||||
 | 
			
		||||
	// Send "quit" message to the reader goroutine
 | 
			
		||||
	ch := make(chan error)
 | 
			
		||||
	w.quit <- ch
 | 
			
		||||
	if err := w.wakeupReader(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return <-ch
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add starts watching the named file or directory (non-recursively).
 | 
			
		||||
func (w *Watcher) Add(name string) error {
 | 
			
		||||
	if w.isClosed {
 | 
			
		||||
		return errors.New("watcher already closed")
 | 
			
		||||
	}
 | 
			
		||||
	in := &input{
 | 
			
		||||
		op:    opAddWatch,
 | 
			
		||||
		path:  filepath.Clean(name),
 | 
			
		||||
		flags: sysFSALLEVENTS,
 | 
			
		||||
		reply: make(chan error),
 | 
			
		||||
	}
 | 
			
		||||
	w.input <- in
 | 
			
		||||
	if err := w.wakeupReader(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return <-in.reply
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Remove stops watching the the named file or directory (non-recursively).
 | 
			
		||||
func (w *Watcher) Remove(name string) error {
 | 
			
		||||
	in := &input{
 | 
			
		||||
		op:    opRemoveWatch,
 | 
			
		||||
		path:  filepath.Clean(name),
 | 
			
		||||
		reply: make(chan error),
 | 
			
		||||
	}
 | 
			
		||||
	w.input <- in
 | 
			
		||||
	if err := w.wakeupReader(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return <-in.reply
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// Options for AddWatch
 | 
			
		||||
	sysFSONESHOT = 0x80000000
 | 
			
		||||
	sysFSONLYDIR = 0x1000000
 | 
			
		||||
 | 
			
		||||
	// Events
 | 
			
		||||
	sysFSACCESS     = 0x1
 | 
			
		||||
	sysFSALLEVENTS  = 0xfff
 | 
			
		||||
	sysFSATTRIB     = 0x4
 | 
			
		||||
	sysFSCLOSE      = 0x18
 | 
			
		||||
	sysFSCREATE     = 0x100
 | 
			
		||||
	sysFSDELETE     = 0x200
 | 
			
		||||
	sysFSDELETESELF = 0x400
 | 
			
		||||
	sysFSMODIFY     = 0x2
 | 
			
		||||
	sysFSMOVE       = 0xc0
 | 
			
		||||
	sysFSMOVEDFROM  = 0x40
 | 
			
		||||
	sysFSMOVEDTO    = 0x80
 | 
			
		||||
	sysFSMOVESELF   = 0x800
 | 
			
		||||
 | 
			
		||||
	// Special events
 | 
			
		||||
	sysFSIGNORED   = 0x8000
 | 
			
		||||
	sysFSQOVERFLOW = 0x4000
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func newEvent(name string, mask uint32) Event {
 | 
			
		||||
	e := Event{Name: name}
 | 
			
		||||
	if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO {
 | 
			
		||||
		e.Op |= Create
 | 
			
		||||
	}
 | 
			
		||||
	if mask&sysFSDELETE == sysFSDELETE || mask&sysFSDELETESELF == sysFSDELETESELF {
 | 
			
		||||
		e.Op |= Remove
 | 
			
		||||
	}
 | 
			
		||||
	if mask&sysFSMODIFY == sysFSMODIFY {
 | 
			
		||||
		e.Op |= Write
 | 
			
		||||
	}
 | 
			
		||||
	if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM {
 | 
			
		||||
		e.Op |= Rename
 | 
			
		||||
	}
 | 
			
		||||
	if mask&sysFSATTRIB == sysFSATTRIB {
 | 
			
		||||
		e.Op |= Chmod
 | 
			
		||||
	}
 | 
			
		||||
	return e
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	opAddWatch = iota
 | 
			
		||||
	opRemoveWatch
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	provisional uint64 = 1 << (32 + iota)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type input struct {
 | 
			
		||||
	op    int
 | 
			
		||||
	path  string
 | 
			
		||||
	flags uint32
 | 
			
		||||
	reply chan error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type inode struct {
 | 
			
		||||
	handle syscall.Handle
 | 
			
		||||
	volume uint32
 | 
			
		||||
	index  uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type watch struct {
 | 
			
		||||
	ov     syscall.Overlapped
 | 
			
		||||
	ino    *inode            // i-number
 | 
			
		||||
	path   string            // Directory path
 | 
			
		||||
	mask   uint64            // Directory itself is being watched with these notify flags
 | 
			
		||||
	names  map[string]uint64 // Map of names being watched and their notify flags
 | 
			
		||||
	rename string            // Remembers the old name while renaming a file
 | 
			
		||||
	buf    [4096]byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type indexMap map[uint64]*watch
 | 
			
		||||
type watchMap map[uint32]indexMap
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) wakeupReader() error {
 | 
			
		||||
	e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil)
 | 
			
		||||
	if e != nil {
 | 
			
		||||
		return os.NewSyscallError("PostQueuedCompletionStatus", e)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getDir(pathname string) (dir string, err error) {
 | 
			
		||||
	attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname))
 | 
			
		||||
	if e != nil {
 | 
			
		||||
		return "", os.NewSyscallError("GetFileAttributes", e)
 | 
			
		||||
	}
 | 
			
		||||
	if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
 | 
			
		||||
		dir = pathname
 | 
			
		||||
	} else {
 | 
			
		||||
		dir, _ = filepath.Split(pathname)
 | 
			
		||||
		dir = filepath.Clean(dir)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getIno(path string) (ino *inode, err error) {
 | 
			
		||||
	h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path),
 | 
			
		||||
		syscall.FILE_LIST_DIRECTORY,
 | 
			
		||||
		syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
 | 
			
		||||
		nil, syscall.OPEN_EXISTING,
 | 
			
		||||
		syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0)
 | 
			
		||||
	if e != nil {
 | 
			
		||||
		return nil, os.NewSyscallError("CreateFile", e)
 | 
			
		||||
	}
 | 
			
		||||
	var fi syscall.ByHandleFileInformation
 | 
			
		||||
	if e = syscall.GetFileInformationByHandle(h, &fi); e != nil {
 | 
			
		||||
		syscall.CloseHandle(h)
 | 
			
		||||
		return nil, os.NewSyscallError("GetFileInformationByHandle", e)
 | 
			
		||||
	}
 | 
			
		||||
	ino = &inode{
 | 
			
		||||
		handle: h,
 | 
			
		||||
		volume: fi.VolumeSerialNumber,
 | 
			
		||||
		index:  uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow),
 | 
			
		||||
	}
 | 
			
		||||
	return ino, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Must run within the I/O thread.
 | 
			
		||||
func (m watchMap) get(ino *inode) *watch {
 | 
			
		||||
	if i := m[ino.volume]; i != nil {
 | 
			
		||||
		return i[ino.index]
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Must run within the I/O thread.
 | 
			
		||||
func (m watchMap) set(ino *inode, watch *watch) {
 | 
			
		||||
	i := m[ino.volume]
 | 
			
		||||
	if i == nil {
 | 
			
		||||
		i = make(indexMap)
 | 
			
		||||
		m[ino.volume] = i
 | 
			
		||||
	}
 | 
			
		||||
	i[ino.index] = watch
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Must run within the I/O thread.
 | 
			
		||||
func (w *Watcher) addWatch(pathname string, flags uint64) error {
 | 
			
		||||
	dir, err := getDir(pathname)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if flags&sysFSONLYDIR != 0 && pathname != dir {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	ino, err := getIno(dir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	watchEntry := w.watches.get(ino)
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
	if watchEntry == nil {
 | 
			
		||||
		if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil {
 | 
			
		||||
			syscall.CloseHandle(ino.handle)
 | 
			
		||||
			return os.NewSyscallError("CreateIoCompletionPort", e)
 | 
			
		||||
		}
 | 
			
		||||
		watchEntry = &watch{
 | 
			
		||||
			ino:   ino,
 | 
			
		||||
			path:  dir,
 | 
			
		||||
			names: make(map[string]uint64),
 | 
			
		||||
		}
 | 
			
		||||
		w.mu.Lock()
 | 
			
		||||
		w.watches.set(ino, watchEntry)
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
		flags |= provisional
 | 
			
		||||
	} else {
 | 
			
		||||
		syscall.CloseHandle(ino.handle)
 | 
			
		||||
	}
 | 
			
		||||
	if pathname == dir {
 | 
			
		||||
		watchEntry.mask |= flags
 | 
			
		||||
	} else {
 | 
			
		||||
		watchEntry.names[filepath.Base(pathname)] |= flags
 | 
			
		||||
	}
 | 
			
		||||
	if err = w.startRead(watchEntry); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if pathname == dir {
 | 
			
		||||
		watchEntry.mask &= ^provisional
 | 
			
		||||
	} else {
 | 
			
		||||
		watchEntry.names[filepath.Base(pathname)] &= ^provisional
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Must run within the I/O thread.
 | 
			
		||||
func (w *Watcher) remWatch(pathname string) error {
 | 
			
		||||
	dir, err := getDir(pathname)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	ino, err := getIno(dir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	w.mu.Lock()
 | 
			
		||||
	watch := w.watches.get(ino)
 | 
			
		||||
	w.mu.Unlock()
 | 
			
		||||
	if watch == nil {
 | 
			
		||||
		return fmt.Errorf("can't remove non-existent watch for: %s", pathname)
 | 
			
		||||
	}
 | 
			
		||||
	if pathname == dir {
 | 
			
		||||
		w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
 | 
			
		||||
		watch.mask = 0
 | 
			
		||||
	} else {
 | 
			
		||||
		name := filepath.Base(pathname)
 | 
			
		||||
		w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED)
 | 
			
		||||
		delete(watch.names, name)
 | 
			
		||||
	}
 | 
			
		||||
	return w.startRead(watch)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Must run within the I/O thread.
 | 
			
		||||
func (w *Watcher) deleteWatch(watch *watch) {
 | 
			
		||||
	for name, mask := range watch.names {
 | 
			
		||||
		if mask&provisional == 0 {
 | 
			
		||||
			w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED)
 | 
			
		||||
		}
 | 
			
		||||
		delete(watch.names, name)
 | 
			
		||||
	}
 | 
			
		||||
	if watch.mask != 0 {
 | 
			
		||||
		if watch.mask&provisional == 0 {
 | 
			
		||||
			w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
 | 
			
		||||
		}
 | 
			
		||||
		watch.mask = 0
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Must run within the I/O thread.
 | 
			
		||||
func (w *Watcher) startRead(watch *watch) error {
 | 
			
		||||
	if e := syscall.CancelIo(watch.ino.handle); e != nil {
 | 
			
		||||
		w.Errors <- os.NewSyscallError("CancelIo", e)
 | 
			
		||||
		w.deleteWatch(watch)
 | 
			
		||||
	}
 | 
			
		||||
	mask := toWindowsFlags(watch.mask)
 | 
			
		||||
	for _, m := range watch.names {
 | 
			
		||||
		mask |= toWindowsFlags(m)
 | 
			
		||||
	}
 | 
			
		||||
	if mask == 0 {
 | 
			
		||||
		if e := syscall.CloseHandle(watch.ino.handle); e != nil {
 | 
			
		||||
			w.Errors <- os.NewSyscallError("CloseHandle", e)
 | 
			
		||||
		}
 | 
			
		||||
		w.mu.Lock()
 | 
			
		||||
		delete(w.watches[watch.ino.volume], watch.ino.index)
 | 
			
		||||
		w.mu.Unlock()
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],
 | 
			
		||||
		uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)
 | 
			
		||||
	if e != nil {
 | 
			
		||||
		err := os.NewSyscallError("ReadDirectoryChanges", e)
 | 
			
		||||
		if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
 | 
			
		||||
			// Watched directory was probably removed
 | 
			
		||||
			if w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) {
 | 
			
		||||
				if watch.mask&sysFSONESHOT != 0 {
 | 
			
		||||
					watch.mask = 0
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			err = nil
 | 
			
		||||
		}
 | 
			
		||||
		w.deleteWatch(watch)
 | 
			
		||||
		w.startRead(watch)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readEvents reads from the I/O completion port, converts the
 | 
			
		||||
// received events into Event objects and sends them via the Events channel.
 | 
			
		||||
// Entry point to the I/O thread.
 | 
			
		||||
func (w *Watcher) readEvents() {
 | 
			
		||||
	var (
 | 
			
		||||
		n, key uint32
 | 
			
		||||
		ov     *syscall.Overlapped
 | 
			
		||||
	)
 | 
			
		||||
	runtime.LockOSThread()
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE)
 | 
			
		||||
		watch := (*watch)(unsafe.Pointer(ov))
 | 
			
		||||
 | 
			
		||||
		if watch == nil {
 | 
			
		||||
			select {
 | 
			
		||||
			case ch := <-w.quit:
 | 
			
		||||
				w.mu.Lock()
 | 
			
		||||
				var indexes []indexMap
 | 
			
		||||
				for _, index := range w.watches {
 | 
			
		||||
					indexes = append(indexes, index)
 | 
			
		||||
				}
 | 
			
		||||
				w.mu.Unlock()
 | 
			
		||||
				for _, index := range indexes {
 | 
			
		||||
					for _, watch := range index {
 | 
			
		||||
						w.deleteWatch(watch)
 | 
			
		||||
						w.startRead(watch)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				var err error
 | 
			
		||||
				if e := syscall.CloseHandle(w.port); e != nil {
 | 
			
		||||
					err = os.NewSyscallError("CloseHandle", e)
 | 
			
		||||
				}
 | 
			
		||||
				close(w.Events)
 | 
			
		||||
				close(w.Errors)
 | 
			
		||||
				ch <- err
 | 
			
		||||
				return
 | 
			
		||||
			case in := <-w.input:
 | 
			
		||||
				switch in.op {
 | 
			
		||||
				case opAddWatch:
 | 
			
		||||
					in.reply <- w.addWatch(in.path, uint64(in.flags))
 | 
			
		||||
				case opRemoveWatch:
 | 
			
		||||
					in.reply <- w.remWatch(in.path)
 | 
			
		||||
				}
 | 
			
		||||
			default:
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch e {
 | 
			
		||||
		case syscall.ERROR_MORE_DATA:
 | 
			
		||||
			if watch == nil {
 | 
			
		||||
				w.Errors <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer")
 | 
			
		||||
			} else {
 | 
			
		||||
				// The i/o succeeded but the buffer is full.
 | 
			
		||||
				// In theory we should be building up a full packet.
 | 
			
		||||
				// In practice we can get away with just carrying on.
 | 
			
		||||
				n = uint32(unsafe.Sizeof(watch.buf))
 | 
			
		||||
			}
 | 
			
		||||
		case syscall.ERROR_ACCESS_DENIED:
 | 
			
		||||
			// Watched directory was probably removed
 | 
			
		||||
			w.sendEvent(watch.path, watch.mask&sysFSDELETESELF)
 | 
			
		||||
			w.deleteWatch(watch)
 | 
			
		||||
			w.startRead(watch)
 | 
			
		||||
			continue
 | 
			
		||||
		case syscall.ERROR_OPERATION_ABORTED:
 | 
			
		||||
			// CancelIo was called on this handle
 | 
			
		||||
			continue
 | 
			
		||||
		default:
 | 
			
		||||
			w.Errors <- os.NewSyscallError("GetQueuedCompletionPort", e)
 | 
			
		||||
			continue
 | 
			
		||||
		case nil:
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var offset uint32
 | 
			
		||||
		for {
 | 
			
		||||
			if n == 0 {
 | 
			
		||||
				w.Events <- newEvent("", sysFSQOVERFLOW)
 | 
			
		||||
				w.Errors <- errors.New("short read in readEvents()")
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Point "raw" to the event in the buffer
 | 
			
		||||
			raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))
 | 
			
		||||
			buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName))
 | 
			
		||||
			name := syscall.UTF16ToString(buf[:raw.FileNameLength/2])
 | 
			
		||||
			fullname := filepath.Join(watch.path, name)
 | 
			
		||||
 | 
			
		||||
			var mask uint64
 | 
			
		||||
			switch raw.Action {
 | 
			
		||||
			case syscall.FILE_ACTION_REMOVED:
 | 
			
		||||
				mask = sysFSDELETESELF
 | 
			
		||||
			case syscall.FILE_ACTION_MODIFIED:
 | 
			
		||||
				mask = sysFSMODIFY
 | 
			
		||||
			case syscall.FILE_ACTION_RENAMED_OLD_NAME:
 | 
			
		||||
				watch.rename = name
 | 
			
		||||
			case syscall.FILE_ACTION_RENAMED_NEW_NAME:
 | 
			
		||||
				if watch.names[watch.rename] != 0 {
 | 
			
		||||
					watch.names[name] |= watch.names[watch.rename]
 | 
			
		||||
					delete(watch.names, watch.rename)
 | 
			
		||||
					mask = sysFSMOVESELF
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			sendNameEvent := func() {
 | 
			
		||||
				if w.sendEvent(fullname, watch.names[name]&mask) {
 | 
			
		||||
					if watch.names[name]&sysFSONESHOT != 0 {
 | 
			
		||||
						delete(watch.names, name)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME {
 | 
			
		||||
				sendNameEvent()
 | 
			
		||||
			}
 | 
			
		||||
			if raw.Action == syscall.FILE_ACTION_REMOVED {
 | 
			
		||||
				w.sendEvent(fullname, watch.names[name]&sysFSIGNORED)
 | 
			
		||||
				delete(watch.names, name)
 | 
			
		||||
			}
 | 
			
		||||
			if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) {
 | 
			
		||||
				if watch.mask&sysFSONESHOT != 0 {
 | 
			
		||||
					watch.mask = 0
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME {
 | 
			
		||||
				fullname = filepath.Join(watch.path, watch.rename)
 | 
			
		||||
				sendNameEvent()
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Move to the next event in the buffer
 | 
			
		||||
			if raw.NextEntryOffset == 0 {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			offset += raw.NextEntryOffset
 | 
			
		||||
 | 
			
		||||
			// Error!
 | 
			
		||||
			if offset >= n {
 | 
			
		||||
				w.Errors <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.")
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := w.startRead(watch); err != nil {
 | 
			
		||||
			w.Errors <- err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *Watcher) sendEvent(name string, mask uint64) bool {
 | 
			
		||||
	if mask == 0 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	event := newEvent(name, uint32(mask))
 | 
			
		||||
	select {
 | 
			
		||||
	case ch := <-w.quit:
 | 
			
		||||
		w.quit <- ch
 | 
			
		||||
	case w.Events <- event:
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func toWindowsFlags(mask uint64) uint32 {
 | 
			
		||||
	var m uint32
 | 
			
		||||
	if mask&sysFSACCESS != 0 {
 | 
			
		||||
		m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS
 | 
			
		||||
	}
 | 
			
		||||
	if mask&sysFSMODIFY != 0 {
 | 
			
		||||
		m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE
 | 
			
		||||
	}
 | 
			
		||||
	if mask&sysFSATTRIB != 0 {
 | 
			
		||||
		m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES
 | 
			
		||||
	}
 | 
			
		||||
	if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 {
 | 
			
		||||
		m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME
 | 
			
		||||
	}
 | 
			
		||||
	return m
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func toFSnotifyFlags(action uint32) uint64 {
 | 
			
		||||
	switch action {
 | 
			
		||||
	case syscall.FILE_ACTION_ADDED:
 | 
			
		||||
		return sysFSCREATE
 | 
			
		||||
	case syscall.FILE_ACTION_REMOVED:
 | 
			
		||||
		return sysFSDELETE
 | 
			
		||||
	case syscall.FILE_ACTION_MODIFIED:
 | 
			
		||||
		return sysFSMODIFY
 | 
			
		||||
	case syscall.FILE_ACTION_RENAMED_OLD_NAME:
 | 
			
		||||
		return sysFSMOVEDFROM
 | 
			
		||||
	case syscall.FILE_ACTION_RENAMED_NEW_NAME:
 | 
			
		||||
		return sysFSMOVEDTO
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are
 | 
			
		||||
met:
 | 
			
		||||
 | 
			
		||||
	 * Redistributions of source code must retain the above copyright
 | 
			
		||||
notice, this list of conditions and the following disclaimer.
 | 
			
		||||
	 * Redistributions in binary form must reproduce the above
 | 
			
		||||
copyright notice, this list of conditions and the following disclaimer
 | 
			
		||||
in the documentation and/or other materials provided with the
 | 
			
		||||
distribution.
 | 
			
		||||
	 * Neither the name of Google Inc. nor the names of its
 | 
			
		||||
contributors may be used to endorse or promote products derived from
 | 
			
		||||
this software without specific prior written permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
			
		||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
			
		||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
			
		||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
context
 | 
			
		||||
=======
 | 
			
		||||
[](https://travis-ci.org/gorilla/context)
 | 
			
		||||
 | 
			
		||||
gorilla/context is a general purpose registry for global request variables.
 | 
			
		||||
 | 
			
		||||
> Note: gorilla/context, having been born well before `context.Context` existed, does not play well
 | 
			
		||||
> with the shallow copying of the request that [`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext) (added to net/http Go 1.7 onwards) performs. You should either use *just* gorilla/context, or moving forward, the new `http.Request.Context()`.
 | 
			
		||||
 | 
			
		||||
Read the full documentation here: http://www.gorillatoolkit.org/pkg/context
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,143 @@
 | 
			
		|||
// Copyright 2012 The Gorilla Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package context
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	mutex sync.RWMutex
 | 
			
		||||
	data  = make(map[*http.Request]map[interface{}]interface{})
 | 
			
		||||
	datat = make(map[*http.Request]int64)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Set stores a value for a given key in a given request.
 | 
			
		||||
func Set(r *http.Request, key, val interface{}) {
 | 
			
		||||
	mutex.Lock()
 | 
			
		||||
	if data[r] == nil {
 | 
			
		||||
		data[r] = make(map[interface{}]interface{})
 | 
			
		||||
		datat[r] = time.Now().Unix()
 | 
			
		||||
	}
 | 
			
		||||
	data[r][key] = val
 | 
			
		||||
	mutex.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get returns a value stored for a given key in a given request.
 | 
			
		||||
func Get(r *http.Request, key interface{}) interface{} {
 | 
			
		||||
	mutex.RLock()
 | 
			
		||||
	if ctx := data[r]; ctx != nil {
 | 
			
		||||
		value := ctx[key]
 | 
			
		||||
		mutex.RUnlock()
 | 
			
		||||
		return value
 | 
			
		||||
	}
 | 
			
		||||
	mutex.RUnlock()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetOk returns stored value and presence state like multi-value return of map access.
 | 
			
		||||
func GetOk(r *http.Request, key interface{}) (interface{}, bool) {
 | 
			
		||||
	mutex.RLock()
 | 
			
		||||
	if _, ok := data[r]; ok {
 | 
			
		||||
		value, ok := data[r][key]
 | 
			
		||||
		mutex.RUnlock()
 | 
			
		||||
		return value, ok
 | 
			
		||||
	}
 | 
			
		||||
	mutex.RUnlock()
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests.
 | 
			
		||||
func GetAll(r *http.Request) map[interface{}]interface{} {
 | 
			
		||||
	mutex.RLock()
 | 
			
		||||
	if context, ok := data[r]; ok {
 | 
			
		||||
		result := make(map[interface{}]interface{}, len(context))
 | 
			
		||||
		for k, v := range context {
 | 
			
		||||
			result[k] = v
 | 
			
		||||
		}
 | 
			
		||||
		mutex.RUnlock()
 | 
			
		||||
		return result
 | 
			
		||||
	}
 | 
			
		||||
	mutex.RUnlock()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAllOk returns all stored values for the request as a map and a boolean value that indicates if
 | 
			
		||||
// the request was registered.
 | 
			
		||||
func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) {
 | 
			
		||||
	mutex.RLock()
 | 
			
		||||
	context, ok := data[r]
 | 
			
		||||
	result := make(map[interface{}]interface{}, len(context))
 | 
			
		||||
	for k, v := range context {
 | 
			
		||||
		result[k] = v
 | 
			
		||||
	}
 | 
			
		||||
	mutex.RUnlock()
 | 
			
		||||
	return result, ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete removes a value stored for a given key in a given request.
 | 
			
		||||
func Delete(r *http.Request, key interface{}) {
 | 
			
		||||
	mutex.Lock()
 | 
			
		||||
	if data[r] != nil {
 | 
			
		||||
		delete(data[r], key)
 | 
			
		||||
	}
 | 
			
		||||
	mutex.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Clear removes all values stored for a given request.
 | 
			
		||||
//
 | 
			
		||||
// This is usually called by a handler wrapper to clean up request
 | 
			
		||||
// variables at the end of a request lifetime. See ClearHandler().
 | 
			
		||||
func Clear(r *http.Request) {
 | 
			
		||||
	mutex.Lock()
 | 
			
		||||
	clear(r)
 | 
			
		||||
	mutex.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// clear is Clear without the lock.
 | 
			
		||||
func clear(r *http.Request) {
 | 
			
		||||
	delete(data, r)
 | 
			
		||||
	delete(datat, r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Purge removes request data stored for longer than maxAge, in seconds.
 | 
			
		||||
// It returns the amount of requests removed.
 | 
			
		||||
//
 | 
			
		||||
// If maxAge <= 0, all request data is removed.
 | 
			
		||||
//
 | 
			
		||||
// This is only used for sanity check: in case context cleaning was not
 | 
			
		||||
// properly set some request data can be kept forever, consuming an increasing
 | 
			
		||||
// amount of memory. In case this is detected, Purge() must be called
 | 
			
		||||
// periodically until the problem is fixed.
 | 
			
		||||
func Purge(maxAge int) int {
 | 
			
		||||
	mutex.Lock()
 | 
			
		||||
	count := 0
 | 
			
		||||
	if maxAge <= 0 {
 | 
			
		||||
		count = len(data)
 | 
			
		||||
		data = make(map[*http.Request]map[interface{}]interface{})
 | 
			
		||||
		datat = make(map[*http.Request]int64)
 | 
			
		||||
	} else {
 | 
			
		||||
		min := time.Now().Unix() - int64(maxAge)
 | 
			
		||||
		for r := range data {
 | 
			
		||||
			if datat[r] < min {
 | 
			
		||||
				clear(r)
 | 
			
		||||
				count++
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	mutex.Unlock()
 | 
			
		||||
	return count
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClearHandler wraps an http.Handler and clears request values at the end
 | 
			
		||||
// of a request lifetime.
 | 
			
		||||
func ClearHandler(h http.Handler) http.Handler {
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		defer Clear(r)
 | 
			
		||||
		h.ServeHTTP(w, r)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,88 @@
 | 
			
		|||
// Copyright 2012 The Gorilla Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Package context stores values shared during a request lifetime.
 | 
			
		||||
 | 
			
		||||
Note: gorilla/context, having been born well before `context.Context` existed,
 | 
			
		||||
does not play well > with the shallow copying of the request that
 | 
			
		||||
[`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext)
 | 
			
		||||
(added to net/http Go 1.7 onwards) performs. You should either use *just*
 | 
			
		||||
gorilla/context, or moving forward, the new `http.Request.Context()`.
 | 
			
		||||
 | 
			
		||||
For example, a router can set variables extracted from the URL and later
 | 
			
		||||
application handlers can access those values, or it can be used to store
 | 
			
		||||
sessions values to be saved at the end of a request. There are several
 | 
			
		||||
others common uses.
 | 
			
		||||
 | 
			
		||||
The idea was posted by Brad Fitzpatrick to the go-nuts mailing list:
 | 
			
		||||
 | 
			
		||||
	http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53
 | 
			
		||||
 | 
			
		||||
Here's the basic usage: first define the keys that you will need. The key
 | 
			
		||||
type is interface{} so a key can be of any type that supports equality.
 | 
			
		||||
Here we define a key using a custom int type to avoid name collisions:
 | 
			
		||||
 | 
			
		||||
	package foo
 | 
			
		||||
 | 
			
		||||
	import (
 | 
			
		||||
		"github.com/gorilla/context"
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	type key int
 | 
			
		||||
 | 
			
		||||
	const MyKey key = 0
 | 
			
		||||
 | 
			
		||||
Then set a variable. Variables are bound to an http.Request object, so you
 | 
			
		||||
need a request instance to set a value:
 | 
			
		||||
 | 
			
		||||
	context.Set(r, MyKey, "bar")
 | 
			
		||||
 | 
			
		||||
The application can later access the variable using the same key you provided:
 | 
			
		||||
 | 
			
		||||
	func MyHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		// val is "bar".
 | 
			
		||||
		val := context.Get(r, foo.MyKey)
 | 
			
		||||
 | 
			
		||||
		// returns ("bar", true)
 | 
			
		||||
		val, ok := context.GetOk(r, foo.MyKey)
 | 
			
		||||
		// ...
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
And that's all about the basic usage. We discuss some other ideas below.
 | 
			
		||||
 | 
			
		||||
Any type can be stored in the context. To enforce a given type, make the key
 | 
			
		||||
private and wrap Get() and Set() to accept and return values of a specific
 | 
			
		||||
type:
 | 
			
		||||
 | 
			
		||||
	type key int
 | 
			
		||||
 | 
			
		||||
	const mykey key = 0
 | 
			
		||||
 | 
			
		||||
	// GetMyKey returns a value for this package from the request values.
 | 
			
		||||
	func GetMyKey(r *http.Request) SomeType {
 | 
			
		||||
		if rv := context.Get(r, mykey); rv != nil {
 | 
			
		||||
			return rv.(SomeType)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// SetMyKey sets a value for this package in the request values.
 | 
			
		||||
	func SetMyKey(r *http.Request, val SomeType) {
 | 
			
		||||
		context.Set(r, mykey, val)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
Variables must be cleared at the end of a request, to remove all values
 | 
			
		||||
that were stored. This can be done in an http.Handler, after a request was
 | 
			
		||||
served. Just call Clear() passing the request:
 | 
			
		||||
 | 
			
		||||
	context.Clear(r)
 | 
			
		||||
 | 
			
		||||
...or use ClearHandler(), which conveniently wraps an http.Handler to clear
 | 
			
		||||
variables at the end of a request lifetime.
 | 
			
		||||
 | 
			
		||||
The Routers from the packages gorilla/mux and gorilla/pat call Clear()
 | 
			
		||||
so if you are using either of them you don't need to clear the context manually.
 | 
			
		||||
*/
 | 
			
		||||
package context
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are
 | 
			
		||||
met:
 | 
			
		||||
 | 
			
		||||
	 * Redistributions of source code must retain the above copyright
 | 
			
		||||
notice, this list of conditions and the following disclaimer.
 | 
			
		||||
	 * Redistributions in binary form must reproduce the above
 | 
			
		||||
copyright notice, this list of conditions and the following disclaimer
 | 
			
		||||
in the documentation and/or other materials provided with the
 | 
			
		||||
distribution.
 | 
			
		||||
	 * Neither the name of Google Inc. nor the names of its
 | 
			
		||||
contributors may be used to endorse or promote products derived from
 | 
			
		||||
this software without specific prior written permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
			
		||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
			
		||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
			
		||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,80 @@
 | 
			
		|||
securecookie
 | 
			
		||||
============
 | 
			
		||||
[](https://godoc.org/github.com/gorilla/securecookie) [](https://travis-ci.org/gorilla/securecookie)
 | 
			
		||||
[](https://sourcegraph.com/github.com/gorilla/securecookie?badge)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
securecookie encodes and decodes authenticated and optionally encrypted 
 | 
			
		||||
cookie values.
 | 
			
		||||
 | 
			
		||||
Secure cookies can't be forged, because their values are validated using HMAC.
 | 
			
		||||
When encrypted, the content is also inaccessible to malicious eyes. It is still
 | 
			
		||||
recommended that sensitive data not be stored in cookies, and that HTTPS be used
 | 
			
		||||
to prevent cookie [replay attacks](https://en.wikipedia.org/wiki/Replay_attack).
 | 
			
		||||
 | 
			
		||||
## Examples
 | 
			
		||||
 | 
			
		||||
To use it, first create a new SecureCookie instance:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
// Hash keys should be at least 32 bytes long
 | 
			
		||||
var hashKey = []byte("very-secret")
 | 
			
		||||
// Block keys should be 16 bytes (AES-128) or 32 bytes (AES-256) long.
 | 
			
		||||
// Shorter keys may weaken the encryption used.
 | 
			
		||||
var blockKey = []byte("a-lot-secret")
 | 
			
		||||
var s = securecookie.New(hashKey, blockKey)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The hashKey is required, used to authenticate the cookie value using HMAC.
 | 
			
		||||
It is recommended to use a key with 32 or 64 bytes.
 | 
			
		||||
 | 
			
		||||
The blockKey is optional, used to encrypt the cookie value -- set it to nil
 | 
			
		||||
to not use encryption. If set, the length must correspond to the block size
 | 
			
		||||
of the encryption algorithm. For AES, used by default, valid lengths are
 | 
			
		||||
16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
 | 
			
		||||
 | 
			
		||||
Strong keys can be created using the convenience function GenerateRandomKey().
 | 
			
		||||
 | 
			
		||||
Once a SecureCookie instance is set, use it to encode a cookie value:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
func SetCookieHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	value := map[string]string{
 | 
			
		||||
		"foo": "bar",
 | 
			
		||||
	}
 | 
			
		||||
	if encoded, err := s.Encode("cookie-name", value); err == nil {
 | 
			
		||||
		cookie := &http.Cookie{
 | 
			
		||||
			Name:  "cookie-name",
 | 
			
		||||
			Value: encoded,
 | 
			
		||||
			Path:  "/",
 | 
			
		||||
			Secure: true,
 | 
			
		||||
			HttpOnly: true,
 | 
			
		||||
		}
 | 
			
		||||
		http.SetCookie(w, cookie)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Later, use the same SecureCookie instance to decode and validate a cookie
 | 
			
		||||
value:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
func ReadCookieHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	if cookie, err := r.Cookie("cookie-name"); err == nil {
 | 
			
		||||
		value := make(map[string]string)
 | 
			
		||||
		if err = s2.Decode("cookie-name", cookie.Value, &value); err == nil {
 | 
			
		||||
			fmt.Fprintf(w, "The value of foo is %q", value["foo"])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
We stored a map[string]string, but secure cookies can hold any value that
 | 
			
		||||
can be encoded using `encoding/gob`. To store custom types, they must be
 | 
			
		||||
registered first using gob.Register(). For basic types this is not needed;
 | 
			
		||||
it works out of the box. An optional JSON encoder that uses `encoding/json` is
 | 
			
		||||
available for types compatible with JSON.
 | 
			
		||||
 | 
			
		||||
## License
 | 
			
		||||
 | 
			
		||||
BSD licensed. See the LICENSE file for details.
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,61 @@
 | 
			
		|||
// Copyright 2012 The Gorilla Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Package securecookie encodes and decodes authenticated and optionally
 | 
			
		||||
encrypted cookie values.
 | 
			
		||||
 | 
			
		||||
Secure cookies can't be forged, because their values are validated using HMAC.
 | 
			
		||||
When encrypted, the content is also inaccessible to malicious eyes.
 | 
			
		||||
 | 
			
		||||
To use it, first create a new SecureCookie instance:
 | 
			
		||||
 | 
			
		||||
	var hashKey = []byte("very-secret")
 | 
			
		||||
	var blockKey = []byte("a-lot-secret")
 | 
			
		||||
	var s = securecookie.New(hashKey, blockKey)
 | 
			
		||||
 | 
			
		||||
The hashKey is required, used to authenticate the cookie value using HMAC.
 | 
			
		||||
It is recommended to use a key with 32 or 64 bytes.
 | 
			
		||||
 | 
			
		||||
The blockKey is optional, used to encrypt the cookie value -- set it to nil
 | 
			
		||||
to not use encryption. If set, the length must correspond to the block size
 | 
			
		||||
of the encryption algorithm. For AES, used by default, valid lengths are
 | 
			
		||||
16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
 | 
			
		||||
 | 
			
		||||
Strong keys can be created using the convenience function GenerateRandomKey().
 | 
			
		||||
 | 
			
		||||
Once a SecureCookie instance is set, use it to encode a cookie value:
 | 
			
		||||
 | 
			
		||||
	func SetCookieHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		value := map[string]string{
 | 
			
		||||
			"foo": "bar",
 | 
			
		||||
		}
 | 
			
		||||
		if encoded, err := s.Encode("cookie-name", value); err == nil {
 | 
			
		||||
			cookie := &http.Cookie{
 | 
			
		||||
				Name:  "cookie-name",
 | 
			
		||||
				Value: encoded,
 | 
			
		||||
				Path:  "/",
 | 
			
		||||
			}
 | 
			
		||||
			http.SetCookie(w, cookie)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
Later, use the same SecureCookie instance to decode and validate a cookie
 | 
			
		||||
value:
 | 
			
		||||
 | 
			
		||||
	func ReadCookieHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		if cookie, err := r.Cookie("cookie-name"); err == nil {
 | 
			
		||||
			value := make(map[string]string)
 | 
			
		||||
			if err = s2.Decode("cookie-name", cookie.Value, &value); err == nil {
 | 
			
		||||
				fmt.Fprintf(w, "The value of foo is %q", value["foo"])
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
We stored a map[string]string, but secure cookies can hold any value that
 | 
			
		||||
can be encoded using encoding/gob. To store custom types, they must be
 | 
			
		||||
registered first using gob.Register(). For basic types this is not needed;
 | 
			
		||||
it works out of the box.
 | 
			
		||||
*/
 | 
			
		||||
package securecookie
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,25 @@
 | 
			
		|||
// +build gofuzz
 | 
			
		||||
 | 
			
		||||
package securecookie
 | 
			
		||||
 | 
			
		||||
var hashKey = []byte("very-secret12345")
 | 
			
		||||
var blockKey = []byte("a-lot-secret1234")
 | 
			
		||||
var s = New(hashKey, blockKey)
 | 
			
		||||
 | 
			
		||||
type Cookie struct {
 | 
			
		||||
	B bool
 | 
			
		||||
	I int
 | 
			
		||||
	S string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Fuzz(data []byte) int {
 | 
			
		||||
	datas := string(data)
 | 
			
		||||
	var c Cookie
 | 
			
		||||
	if err := s.Decode("fuzz", datas, &c); err != nil {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := s.Encode("fuzz", c); err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return 1
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,646 @@
 | 
			
		|||
// Copyright 2012 The Gorilla Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package securecookie
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/aes"
 | 
			
		||||
	"crypto/cipher"
 | 
			
		||||
	"crypto/hmac"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/sha256"
 | 
			
		||||
	"crypto/subtle"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"encoding/gob"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"hash"
 | 
			
		||||
	"io"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Error is the interface of all errors returned by functions in this library.
 | 
			
		||||
type Error interface {
 | 
			
		||||
	error
 | 
			
		||||
 | 
			
		||||
	// IsUsage returns true for errors indicating the client code probably
 | 
			
		||||
	// uses this library incorrectly.  For example, the client may have
 | 
			
		||||
	// failed to provide a valid hash key, or may have failed to configure
 | 
			
		||||
	// the Serializer adequately for encoding value.
 | 
			
		||||
	IsUsage() bool
 | 
			
		||||
 | 
			
		||||
	// IsDecode returns true for errors indicating that a cookie could not
 | 
			
		||||
	// be decoded and validated.  Since cookies are usually untrusted
 | 
			
		||||
	// user-provided input, errors of this type should be expected.
 | 
			
		||||
	// Usually, the proper action is simply to reject the request.
 | 
			
		||||
	IsDecode() bool
 | 
			
		||||
 | 
			
		||||
	// IsInternal returns true for unexpected errors occurring in the
 | 
			
		||||
	// securecookie implementation.
 | 
			
		||||
	IsInternal() bool
 | 
			
		||||
 | 
			
		||||
	// Cause, if it returns a non-nil value, indicates that this error was
 | 
			
		||||
	// propagated from some underlying library.  If this method returns nil,
 | 
			
		||||
	// this error was raised directly by this library.
 | 
			
		||||
	//
 | 
			
		||||
	// Cause is provided principally for debugging/logging purposes; it is
 | 
			
		||||
	// rare that application logic should perform meaningfully different
 | 
			
		||||
	// logic based on Cause.  See, for example, the caveats described on
 | 
			
		||||
	// (MultiError).Cause().
 | 
			
		||||
	Cause() error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// errorType is a bitmask giving the error type(s) of an cookieError value.
 | 
			
		||||
type errorType int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	usageError = errorType(1 << iota)
 | 
			
		||||
	decodeError
 | 
			
		||||
	internalError
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type cookieError struct {
 | 
			
		||||
	typ   errorType
 | 
			
		||||
	msg   string
 | 
			
		||||
	cause error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e cookieError) IsUsage() bool    { return (e.typ & usageError) != 0 }
 | 
			
		||||
func (e cookieError) IsDecode() bool   { return (e.typ & decodeError) != 0 }
 | 
			
		||||
func (e cookieError) IsInternal() bool { return (e.typ & internalError) != 0 }
 | 
			
		||||
 | 
			
		||||
func (e cookieError) Cause() error { return e.cause }
 | 
			
		||||
 | 
			
		||||
func (e cookieError) Error() string {
 | 
			
		||||
	parts := []string{"securecookie: "}
 | 
			
		||||
	if e.msg == "" {
 | 
			
		||||
		parts = append(parts, "error")
 | 
			
		||||
	} else {
 | 
			
		||||
		parts = append(parts, e.msg)
 | 
			
		||||
	}
 | 
			
		||||
	if c := e.Cause(); c != nil {
 | 
			
		||||
		parts = append(parts, " - caused by: ", c.Error())
 | 
			
		||||
	}
 | 
			
		||||
	return strings.Join(parts, "")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	errGeneratingIV = cookieError{typ: internalError, msg: "failed to generate random iv"}
 | 
			
		||||
 | 
			
		||||
	errNoCodecs            = cookieError{typ: usageError, msg: "no codecs provided"}
 | 
			
		||||
	errHashKeyNotSet       = cookieError{typ: usageError, msg: "hash key is not set"}
 | 
			
		||||
	errBlockKeyNotSet      = cookieError{typ: usageError, msg: "block key is not set"}
 | 
			
		||||
	errEncodedValueTooLong = cookieError{typ: usageError, msg: "the value is too long"}
 | 
			
		||||
 | 
			
		||||
	errValueToDecodeTooLong = cookieError{typ: decodeError, msg: "the value is too long"}
 | 
			
		||||
	errTimestampInvalid     = cookieError{typ: decodeError, msg: "invalid timestamp"}
 | 
			
		||||
	errTimestampTooNew      = cookieError{typ: decodeError, msg: "timestamp is too new"}
 | 
			
		||||
	errTimestampExpired     = cookieError{typ: decodeError, msg: "expired timestamp"}
 | 
			
		||||
	errDecryptionFailed     = cookieError{typ: decodeError, msg: "the value could not be decrypted"}
 | 
			
		||||
	errValueNotByte         = cookieError{typ: decodeError, msg: "value not a []byte."}
 | 
			
		||||
	errValueNotBytePtr      = cookieError{typ: decodeError, msg: "value not a pointer to []byte."}
 | 
			
		||||
 | 
			
		||||
	// ErrMacInvalid indicates that cookie decoding failed because the HMAC
 | 
			
		||||
	// could not be extracted and verified.  Direct use of this error
 | 
			
		||||
	// variable is deprecated; it is public only for legacy compatibility,
 | 
			
		||||
	// and may be privatized in the future, as it is rarely useful to
 | 
			
		||||
	// distinguish between this error and other Error implementations.
 | 
			
		||||
	ErrMacInvalid = cookieError{typ: decodeError, msg: "the value is not valid"}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Codec defines an interface to encode and decode cookie values.
 | 
			
		||||
type Codec interface {
 | 
			
		||||
	Encode(name string, value interface{}) (string, error)
 | 
			
		||||
	Decode(name, value string, dst interface{}) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New returns a new SecureCookie.
 | 
			
		||||
//
 | 
			
		||||
// hashKey is required, used to authenticate values using HMAC. Create it using
 | 
			
		||||
// GenerateRandomKey(). It is recommended to use a key with 32 or 64 bytes.
 | 
			
		||||
//
 | 
			
		||||
// blockKey is optional, used to encrypt values. Create it using
 | 
			
		||||
// GenerateRandomKey(). The key length must correspond to the block size
 | 
			
		||||
// of the encryption algorithm. For AES, used by default, valid lengths are
 | 
			
		||||
// 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
 | 
			
		||||
// The default encoder used for cookie serialization is encoding/gob.
 | 
			
		||||
//
 | 
			
		||||
// Note that keys created using GenerateRandomKey() are not automatically
 | 
			
		||||
// persisted. New keys will be created when the application is restarted, and
 | 
			
		||||
// previously issued cookies will not be able to be decoded.
 | 
			
		||||
func New(hashKey, blockKey []byte) *SecureCookie {
 | 
			
		||||
	s := &SecureCookie{
 | 
			
		||||
		hashKey:   hashKey,
 | 
			
		||||
		blockKey:  blockKey,
 | 
			
		||||
		hashFunc:  sha256.New,
 | 
			
		||||
		maxAge:    86400 * 30,
 | 
			
		||||
		maxLength: 4096,
 | 
			
		||||
		sz:        GobEncoder{},
 | 
			
		||||
	}
 | 
			
		||||
	if hashKey == nil {
 | 
			
		||||
		s.err = errHashKeyNotSet
 | 
			
		||||
	}
 | 
			
		||||
	if blockKey != nil {
 | 
			
		||||
		s.BlockFunc(aes.NewCipher)
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SecureCookie encodes and decodes authenticated and optionally encrypted
 | 
			
		||||
// cookie values.
 | 
			
		||||
type SecureCookie struct {
 | 
			
		||||
	hashKey   []byte
 | 
			
		||||
	hashFunc  func() hash.Hash
 | 
			
		||||
	blockKey  []byte
 | 
			
		||||
	block     cipher.Block
 | 
			
		||||
	maxLength int
 | 
			
		||||
	maxAge    int64
 | 
			
		||||
	minAge    int64
 | 
			
		||||
	err       error
 | 
			
		||||
	sz        Serializer
 | 
			
		||||
	// For testing purposes, the function that returns the current timestamp.
 | 
			
		||||
	// If not set, it will use time.Now().UTC().Unix().
 | 
			
		||||
	timeFunc func() int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Serializer provides an interface for providing custom serializers for cookie
 | 
			
		||||
// values.
 | 
			
		||||
type Serializer interface {
 | 
			
		||||
	Serialize(src interface{}) ([]byte, error)
 | 
			
		||||
	Deserialize(src []byte, dst interface{}) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GobEncoder encodes cookie values using encoding/gob. This is the simplest
 | 
			
		||||
// encoder and can handle complex types via gob.Register.
 | 
			
		||||
type GobEncoder struct{}
 | 
			
		||||
 | 
			
		||||
// JSONEncoder encodes cookie values using encoding/json. Users who wish to
 | 
			
		||||
// encode complex types need to satisfy the json.Marshaller and
 | 
			
		||||
// json.Unmarshaller interfaces.
 | 
			
		||||
type JSONEncoder struct{}
 | 
			
		||||
 | 
			
		||||
// NopEncoder does not encode cookie values, and instead simply accepts a []byte
 | 
			
		||||
// (as an interface{}) and returns a []byte. This is particularly useful when
 | 
			
		||||
// you encoding an object upstream and do not wish to re-encode it.
 | 
			
		||||
type NopEncoder struct{}
 | 
			
		||||
 | 
			
		||||
// MaxLength restricts the maximum length, in bytes, for the cookie value.
 | 
			
		||||
//
 | 
			
		||||
// Default is 4096, which is the maximum value accepted by Internet Explorer.
 | 
			
		||||
func (s *SecureCookie) MaxLength(value int) *SecureCookie {
 | 
			
		||||
	s.maxLength = value
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MaxAge restricts the maximum age, in seconds, for the cookie value.
 | 
			
		||||
//
 | 
			
		||||
// Default is 86400 * 30. Set it to 0 for no restriction.
 | 
			
		||||
func (s *SecureCookie) MaxAge(value int) *SecureCookie {
 | 
			
		||||
	s.maxAge = int64(value)
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MinAge restricts the minimum age, in seconds, for the cookie value.
 | 
			
		||||
//
 | 
			
		||||
// Default is 0 (no restriction).
 | 
			
		||||
func (s *SecureCookie) MinAge(value int) *SecureCookie {
 | 
			
		||||
	s.minAge = int64(value)
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HashFunc sets the hash function used to create HMAC.
 | 
			
		||||
//
 | 
			
		||||
// Default is crypto/sha256.New.
 | 
			
		||||
func (s *SecureCookie) HashFunc(f func() hash.Hash) *SecureCookie {
 | 
			
		||||
	s.hashFunc = f
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BlockFunc sets the encryption function used to create a cipher.Block.
 | 
			
		||||
//
 | 
			
		||||
// Default is crypto/aes.New.
 | 
			
		||||
func (s *SecureCookie) BlockFunc(f func([]byte) (cipher.Block, error)) *SecureCookie {
 | 
			
		||||
	if s.blockKey == nil {
 | 
			
		||||
		s.err = errBlockKeyNotSet
 | 
			
		||||
	} else if block, err := f(s.blockKey); err == nil {
 | 
			
		||||
		s.block = block
 | 
			
		||||
	} else {
 | 
			
		||||
		s.err = cookieError{cause: err, typ: usageError}
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Encoding sets the encoding/serialization method for cookies.
 | 
			
		||||
//
 | 
			
		||||
// Default is encoding/gob.  To encode special structures using encoding/gob,
 | 
			
		||||
// they must be registered first using gob.Register().
 | 
			
		||||
func (s *SecureCookie) SetSerializer(sz Serializer) *SecureCookie {
 | 
			
		||||
	s.sz = sz
 | 
			
		||||
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Encode encodes a cookie value.
 | 
			
		||||
//
 | 
			
		||||
// It serializes, optionally encrypts, signs with a message authentication code,
 | 
			
		||||
// and finally encodes the value.
 | 
			
		||||
//
 | 
			
		||||
// The name argument is the cookie name. It is stored with the encoded value.
 | 
			
		||||
// The value argument is the value to be encoded. It can be any value that can
 | 
			
		||||
// be encoded using the currently selected serializer; see SetSerializer().
 | 
			
		||||
//
 | 
			
		||||
// It is the client's responsibility to ensure that value, when encoded using
 | 
			
		||||
// the current serialization/encryption settings on s and then base64-encoded,
 | 
			
		||||
// is shorter than the maximum permissible length.
 | 
			
		||||
func (s *SecureCookie) Encode(name string, value interface{}) (string, error) {
 | 
			
		||||
	if s.err != nil {
 | 
			
		||||
		return "", s.err
 | 
			
		||||
	}
 | 
			
		||||
	if s.hashKey == nil {
 | 
			
		||||
		s.err = errHashKeyNotSet
 | 
			
		||||
		return "", s.err
 | 
			
		||||
	}
 | 
			
		||||
	var err error
 | 
			
		||||
	var b []byte
 | 
			
		||||
	// 1. Serialize.
 | 
			
		||||
	if b, err = s.sz.Serialize(value); err != nil {
 | 
			
		||||
		return "", cookieError{cause: err, typ: usageError}
 | 
			
		||||
	}
 | 
			
		||||
	// 2. Encrypt (optional).
 | 
			
		||||
	if s.block != nil {
 | 
			
		||||
		if b, err = encrypt(s.block, b); err != nil {
 | 
			
		||||
			return "", cookieError{cause: err, typ: usageError}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	b = encode(b)
 | 
			
		||||
	// 3. Create MAC for "name|date|value". Extra pipe to be used later.
 | 
			
		||||
	b = []byte(fmt.Sprintf("%s|%d|%s|", name, s.timestamp(), b))
 | 
			
		||||
	mac := createMac(hmac.New(s.hashFunc, s.hashKey), b[:len(b)-1])
 | 
			
		||||
	// Append mac, remove name.
 | 
			
		||||
	b = append(b, mac...)[len(name)+1:]
 | 
			
		||||
	// 4. Encode to base64.
 | 
			
		||||
	b = encode(b)
 | 
			
		||||
	// 5. Check length.
 | 
			
		||||
	if s.maxLength != 0 && len(b) > s.maxLength {
 | 
			
		||||
		return "", errEncodedValueTooLong
 | 
			
		||||
	}
 | 
			
		||||
	// Done.
 | 
			
		||||
	return string(b), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Decode decodes a cookie value.
 | 
			
		||||
//
 | 
			
		||||
// It decodes, verifies a message authentication code, optionally decrypts and
 | 
			
		||||
// finally deserializes the value.
 | 
			
		||||
//
 | 
			
		||||
// The name argument is the cookie name. It must be the same name used when
 | 
			
		||||
// it was stored. The value argument is the encoded cookie value. The dst
 | 
			
		||||
// argument is where the cookie will be decoded. It must be a pointer.
 | 
			
		||||
func (s *SecureCookie) Decode(name, value string, dst interface{}) error {
 | 
			
		||||
	if s.err != nil {
 | 
			
		||||
		return s.err
 | 
			
		||||
	}
 | 
			
		||||
	if s.hashKey == nil {
 | 
			
		||||
		s.err = errHashKeyNotSet
 | 
			
		||||
		return s.err
 | 
			
		||||
	}
 | 
			
		||||
	// 1. Check length.
 | 
			
		||||
	if s.maxLength != 0 && len(value) > s.maxLength {
 | 
			
		||||
		return errValueToDecodeTooLong
 | 
			
		||||
	}
 | 
			
		||||
	// 2. Decode from base64.
 | 
			
		||||
	b, err := decode([]byte(value))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	// 3. Verify MAC. Value is "date|value|mac".
 | 
			
		||||
	parts := bytes.SplitN(b, []byte("|"), 3)
 | 
			
		||||
	if len(parts) != 3 {
 | 
			
		||||
		return ErrMacInvalid
 | 
			
		||||
	}
 | 
			
		||||
	h := hmac.New(s.hashFunc, s.hashKey)
 | 
			
		||||
	b = append([]byte(name+"|"), b[:len(b)-len(parts[2])-1]...)
 | 
			
		||||
	if err = verifyMac(h, b, parts[2]); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	// 4. Verify date ranges.
 | 
			
		||||
	var t1 int64
 | 
			
		||||
	if t1, err = strconv.ParseInt(string(parts[0]), 10, 64); err != nil {
 | 
			
		||||
		return errTimestampInvalid
 | 
			
		||||
	}
 | 
			
		||||
	t2 := s.timestamp()
 | 
			
		||||
	if s.minAge != 0 && t1 > t2-s.minAge {
 | 
			
		||||
		return errTimestampTooNew
 | 
			
		||||
	}
 | 
			
		||||
	if s.maxAge != 0 && t1 < t2-s.maxAge {
 | 
			
		||||
		return errTimestampExpired
 | 
			
		||||
	}
 | 
			
		||||
	// 5. Decrypt (optional).
 | 
			
		||||
	b, err = decode(parts[1])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if s.block != nil {
 | 
			
		||||
		if b, err = decrypt(s.block, b); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// 6. Deserialize.
 | 
			
		||||
	if err = s.sz.Deserialize(b, dst); err != nil {
 | 
			
		||||
		return cookieError{cause: err, typ: decodeError}
 | 
			
		||||
	}
 | 
			
		||||
	// Done.
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// timestamp returns the current timestamp, in seconds.
 | 
			
		||||
//
 | 
			
		||||
// For testing purposes, the function that generates the timestamp can be
 | 
			
		||||
// overridden. If not set, it will return time.Now().UTC().Unix().
 | 
			
		||||
func (s *SecureCookie) timestamp() int64 {
 | 
			
		||||
	if s.timeFunc == nil {
 | 
			
		||||
		return time.Now().UTC().Unix()
 | 
			
		||||
	}
 | 
			
		||||
	return s.timeFunc()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Authentication -------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// createMac creates a message authentication code (MAC).
 | 
			
		||||
func createMac(h hash.Hash, value []byte) []byte {
 | 
			
		||||
	h.Write(value)
 | 
			
		||||
	return h.Sum(nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// verifyMac verifies that a message authentication code (MAC) is valid.
 | 
			
		||||
func verifyMac(h hash.Hash, value []byte, mac []byte) error {
 | 
			
		||||
	mac2 := createMac(h, value)
 | 
			
		||||
	// Check that both MACs are of equal length, as subtle.ConstantTimeCompare
 | 
			
		||||
	// does not do this prior to Go 1.4.
 | 
			
		||||
	if len(mac) == len(mac2) && subtle.ConstantTimeCompare(mac, mac2) == 1 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return ErrMacInvalid
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Encryption -----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// encrypt encrypts a value using the given block in counter mode.
 | 
			
		||||
//
 | 
			
		||||
// A random initialization vector (http://goo.gl/zF67k) with the length of the
 | 
			
		||||
// block size is prepended to the resulting ciphertext.
 | 
			
		||||
func encrypt(block cipher.Block, value []byte) ([]byte, error) {
 | 
			
		||||
	iv := GenerateRandomKey(block.BlockSize())
 | 
			
		||||
	if iv == nil {
 | 
			
		||||
		return nil, errGeneratingIV
 | 
			
		||||
	}
 | 
			
		||||
	// Encrypt it.
 | 
			
		||||
	stream := cipher.NewCTR(block, iv)
 | 
			
		||||
	stream.XORKeyStream(value, value)
 | 
			
		||||
	// Return iv + ciphertext.
 | 
			
		||||
	return append(iv, value...), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// decrypt decrypts a value using the given block in counter mode.
 | 
			
		||||
//
 | 
			
		||||
// The value to be decrypted must be prepended by a initialization vector
 | 
			
		||||
// (http://goo.gl/zF67k) with the length of the block size.
 | 
			
		||||
func decrypt(block cipher.Block, value []byte) ([]byte, error) {
 | 
			
		||||
	size := block.BlockSize()
 | 
			
		||||
	if len(value) > size {
 | 
			
		||||
		// Extract iv.
 | 
			
		||||
		iv := value[:size]
 | 
			
		||||
		// Extract ciphertext.
 | 
			
		||||
		value = value[size:]
 | 
			
		||||
		// Decrypt it.
 | 
			
		||||
		stream := cipher.NewCTR(block, iv)
 | 
			
		||||
		stream.XORKeyStream(value, value)
 | 
			
		||||
		return value, nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil, errDecryptionFailed
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Serialization --------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// Serialize encodes a value using gob.
 | 
			
		||||
func (e GobEncoder) Serialize(src interface{}) ([]byte, error) {
 | 
			
		||||
	buf := new(bytes.Buffer)
 | 
			
		||||
	enc := gob.NewEncoder(buf)
 | 
			
		||||
	if err := enc.Encode(src); err != nil {
 | 
			
		||||
		return nil, cookieError{cause: err, typ: usageError}
 | 
			
		||||
	}
 | 
			
		||||
	return buf.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Deserialize decodes a value using gob.
 | 
			
		||||
func (e GobEncoder) Deserialize(src []byte, dst interface{}) error {
 | 
			
		||||
	dec := gob.NewDecoder(bytes.NewBuffer(src))
 | 
			
		||||
	if err := dec.Decode(dst); err != nil {
 | 
			
		||||
		return cookieError{cause: err, typ: decodeError}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Serialize encodes a value using encoding/json.
 | 
			
		||||
func (e JSONEncoder) Serialize(src interface{}) ([]byte, error) {
 | 
			
		||||
	buf := new(bytes.Buffer)
 | 
			
		||||
	enc := json.NewEncoder(buf)
 | 
			
		||||
	if err := enc.Encode(src); err != nil {
 | 
			
		||||
		return nil, cookieError{cause: err, typ: usageError}
 | 
			
		||||
	}
 | 
			
		||||
	return buf.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Deserialize decodes a value using encoding/json.
 | 
			
		||||
func (e JSONEncoder) Deserialize(src []byte, dst interface{}) error {
 | 
			
		||||
	dec := json.NewDecoder(bytes.NewReader(src))
 | 
			
		||||
	if err := dec.Decode(dst); err != nil {
 | 
			
		||||
		return cookieError{cause: err, typ: decodeError}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Serialize passes a []byte through as-is.
 | 
			
		||||
func (e NopEncoder) Serialize(src interface{}) ([]byte, error) {
 | 
			
		||||
	if b, ok := src.([]byte); ok {
 | 
			
		||||
		return b, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, errValueNotByte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Deserialize passes a []byte through as-is.
 | 
			
		||||
func (e NopEncoder) Deserialize(src []byte, dst interface{}) error {
 | 
			
		||||
	if dat, ok := dst.(*[]byte); ok {
 | 
			
		||||
		*dat = src
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return errValueNotBytePtr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Encoding -------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// encode encodes a value using base64.
 | 
			
		||||
func encode(value []byte) []byte {
 | 
			
		||||
	encoded := make([]byte, base64.URLEncoding.EncodedLen(len(value)))
 | 
			
		||||
	base64.URLEncoding.Encode(encoded, value)
 | 
			
		||||
	return encoded
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// decode decodes a cookie using base64.
 | 
			
		||||
func decode(value []byte) ([]byte, error) {
 | 
			
		||||
	decoded := make([]byte, base64.URLEncoding.DecodedLen(len(value)))
 | 
			
		||||
	b, err := base64.URLEncoding.Decode(decoded, value)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, cookieError{cause: err, typ: decodeError, msg: "base64 decode failed"}
 | 
			
		||||
	}
 | 
			
		||||
	return decoded[:b], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helpers --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// GenerateRandomKey creates a random key with the given length in bytes.
 | 
			
		||||
// On failure, returns nil.
 | 
			
		||||
//
 | 
			
		||||
// Callers should explicitly check for the possibility of a nil return, treat
 | 
			
		||||
// it as a failure of the system random number generator, and not continue.
 | 
			
		||||
func GenerateRandomKey(length int) []byte {
 | 
			
		||||
	k := make([]byte, length)
 | 
			
		||||
	if _, err := io.ReadFull(rand.Reader, k); err != nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return k
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CodecsFromPairs returns a slice of SecureCookie instances.
 | 
			
		||||
//
 | 
			
		||||
// It is a convenience function to create a list of codecs for key rotation. Note
 | 
			
		||||
// that the generated Codecs will have the default options applied: callers
 | 
			
		||||
// should iterate over each Codec and type-assert the underlying *SecureCookie to
 | 
			
		||||
// change these.
 | 
			
		||||
//
 | 
			
		||||
// Example:
 | 
			
		||||
//
 | 
			
		||||
//      codecs := securecookie.CodecsFromPairs(
 | 
			
		||||
//           []byte("new-hash-key"),
 | 
			
		||||
//           []byte("new-block-key"),
 | 
			
		||||
//           []byte("old-hash-key"),
 | 
			
		||||
//           []byte("old-block-key"),
 | 
			
		||||
//       )
 | 
			
		||||
//
 | 
			
		||||
//      // Modify each instance.
 | 
			
		||||
//      for _, s := range codecs {
 | 
			
		||||
//             if cookie, ok := s.(*securecookie.SecureCookie); ok {
 | 
			
		||||
//                 cookie.MaxAge(86400 * 7)
 | 
			
		||||
//                 cookie.SetSerializer(securecookie.JSONEncoder{})
 | 
			
		||||
//                 cookie.HashFunc(sha512.New512_256)
 | 
			
		||||
//             }
 | 
			
		||||
//         }
 | 
			
		||||
//
 | 
			
		||||
func CodecsFromPairs(keyPairs ...[]byte) []Codec {
 | 
			
		||||
	codecs := make([]Codec, len(keyPairs)/2+len(keyPairs)%2)
 | 
			
		||||
	for i := 0; i < len(keyPairs); i += 2 {
 | 
			
		||||
		var blockKey []byte
 | 
			
		||||
		if i+1 < len(keyPairs) {
 | 
			
		||||
			blockKey = keyPairs[i+1]
 | 
			
		||||
		}
 | 
			
		||||
		codecs[i/2] = New(keyPairs[i], blockKey)
 | 
			
		||||
	}
 | 
			
		||||
	return codecs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EncodeMulti encodes a cookie value using a group of codecs.
 | 
			
		||||
//
 | 
			
		||||
// The codecs are tried in order. Multiple codecs are accepted to allow
 | 
			
		||||
// key rotation.
 | 
			
		||||
//
 | 
			
		||||
// On error, may return a MultiError.
 | 
			
		||||
func EncodeMulti(name string, value interface{}, codecs ...Codec) (string, error) {
 | 
			
		||||
	if len(codecs) == 0 {
 | 
			
		||||
		return "", errNoCodecs
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var errors MultiError
 | 
			
		||||
	for _, codec := range codecs {
 | 
			
		||||
		encoded, err := codec.Encode(name, value)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			return encoded, nil
 | 
			
		||||
		}
 | 
			
		||||
		errors = append(errors, err)
 | 
			
		||||
	}
 | 
			
		||||
	return "", errors
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DecodeMulti decodes a cookie value using a group of codecs.
 | 
			
		||||
//
 | 
			
		||||
// The codecs are tried in order. Multiple codecs are accepted to allow
 | 
			
		||||
// key rotation.
 | 
			
		||||
//
 | 
			
		||||
// On error, may return a MultiError.
 | 
			
		||||
func DecodeMulti(name string, value string, dst interface{}, codecs ...Codec) error {
 | 
			
		||||
	if len(codecs) == 0 {
 | 
			
		||||
		return errNoCodecs
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var errors MultiError
 | 
			
		||||
	for _, codec := range codecs {
 | 
			
		||||
		err := codec.Decode(name, value, dst)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		errors = append(errors, err)
 | 
			
		||||
	}
 | 
			
		||||
	return errors
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MultiError groups multiple errors.
 | 
			
		||||
type MultiError []error
 | 
			
		||||
 | 
			
		||||
func (m MultiError) IsUsage() bool    { return m.any(func(e Error) bool { return e.IsUsage() }) }
 | 
			
		||||
func (m MultiError) IsDecode() bool   { return m.any(func(e Error) bool { return e.IsDecode() }) }
 | 
			
		||||
func (m MultiError) IsInternal() bool { return m.any(func(e Error) bool { return e.IsInternal() }) }
 | 
			
		||||
 | 
			
		||||
// Cause returns nil for MultiError; there is no unique underlying cause in the
 | 
			
		||||
// general case.
 | 
			
		||||
//
 | 
			
		||||
// Note: we could conceivably return a non-nil Cause only when there is exactly
 | 
			
		||||
// one child error with a Cause.  However, it would be brittle for client code
 | 
			
		||||
// to rely on the arity of causes inside a MultiError, so we have opted not to
 | 
			
		||||
// provide this functionality.  Clients which really wish to access the Causes
 | 
			
		||||
// of the underlying errors are free to iterate through the errors themselves.
 | 
			
		||||
func (m MultiError) Cause() error { return nil }
 | 
			
		||||
 | 
			
		||||
func (m MultiError) Error() string {
 | 
			
		||||
	s, n := "", 0
 | 
			
		||||
	for _, e := range m {
 | 
			
		||||
		if e != nil {
 | 
			
		||||
			if n == 0 {
 | 
			
		||||
				s = e.Error()
 | 
			
		||||
			}
 | 
			
		||||
			n++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	switch n {
 | 
			
		||||
	case 0:
 | 
			
		||||
		return "(0 errors)"
 | 
			
		||||
	case 1:
 | 
			
		||||
		return s
 | 
			
		||||
	case 2:
 | 
			
		||||
		return s + " (and 1 other error)"
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s (and %d other errors)", s, n-1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// any returns true if any element of m is an Error for which pred returns true.
 | 
			
		||||
func (m MultiError) any(pred func(Error) bool) bool {
 | 
			
		||||
	for _, e := range m {
 | 
			
		||||
		if ourErr, ok := e.(Error); ok && pred(ourErr) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are
 | 
			
		||||
met:
 | 
			
		||||
 | 
			
		||||
	 * Redistributions of source code must retain the above copyright
 | 
			
		||||
notice, this list of conditions and the following disclaimer.
 | 
			
		||||
	 * Redistributions in binary form must reproduce the above
 | 
			
		||||
copyright notice, this list of conditions and the following disclaimer
 | 
			
		||||
in the documentation and/or other materials provided with the
 | 
			
		||||
distribution.
 | 
			
		||||
	 * Neither the name of Google Inc. nor the names of its
 | 
			
		||||
contributors may be used to endorse or promote products derived from
 | 
			
		||||
this software without specific prior written permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
			
		||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
			
		||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
			
		||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,90 @@
 | 
			
		|||
sessions
 | 
			
		||||
========
 | 
			
		||||
[](https://godoc.org/github.com/gorilla/sessions) [](https://travis-ci.org/gorilla/sessions)
 | 
			
		||||
[](https://sourcegraph.com/github.com/gorilla/sessions?badge)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
gorilla/sessions provides cookie and filesystem sessions and infrastructure for
 | 
			
		||||
custom session backends.
 | 
			
		||||
 | 
			
		||||
The key features are:
 | 
			
		||||
 | 
			
		||||
* Simple API: use it as an easy way to set signed (and optionally
 | 
			
		||||
  encrypted) cookies.
 | 
			
		||||
* Built-in backends to store sessions in cookies or the filesystem.
 | 
			
		||||
* Flash messages: session values that last until read.
 | 
			
		||||
* Convenient way to switch session persistency (aka "remember me") and set
 | 
			
		||||
  other attributes.
 | 
			
		||||
* Mechanism to rotate authentication and encryption keys.
 | 
			
		||||
* Multiple sessions per request, even using different backends.
 | 
			
		||||
* Interfaces and infrastructure for custom session backends: sessions from
 | 
			
		||||
  different stores can be retrieved and batch-saved using a common API.
 | 
			
		||||
 | 
			
		||||
Let's start with an example that shows the sessions API in a nutshell:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
	import (
 | 
			
		||||
		"net/http"
 | 
			
		||||
		"github.com/gorilla/sessions"
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	var store = sessions.NewCookieStore([]byte("something-very-secret"))
 | 
			
		||||
 | 
			
		||||
	func MyHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		// Get a session. We're ignoring the error resulted from decoding an
 | 
			
		||||
		// existing session: Get() always returns a session, even if empty.
 | 
			
		||||
		session, _ := store.Get(r, "session-name")
 | 
			
		||||
		// Set some session values.
 | 
			
		||||
		session.Values["foo"] = "bar"
 | 
			
		||||
		session.Values[42] = 43
 | 
			
		||||
		// Save it before we write to the response/return from the handler.
 | 
			
		||||
		session.Save(r, w)
 | 
			
		||||
	}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
First we initialize a session store calling `NewCookieStore()` and passing a
 | 
			
		||||
secret key used to authenticate the session. Inside the handler, we call
 | 
			
		||||
`store.Get()` to retrieve an existing session or a new one. Then we set some
 | 
			
		||||
session values in session.Values, which is a `map[interface{}]interface{}`.
 | 
			
		||||
And finally we call `session.Save()` to save the session in the response.
 | 
			
		||||
 | 
			
		||||
Important Note: If you aren't using gorilla/mux, you need to wrap your handlers
 | 
			
		||||
with
 | 
			
		||||
[`context.ClearHandler`](http://www.gorillatoolkit.org/pkg/context#ClearHandler)
 | 
			
		||||
as or else you will leak memory! An easy way to do this is to wrap the top-level
 | 
			
		||||
mux when calling http.ListenAndServe:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
	http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux))
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The ClearHandler function is provided by the gorilla/context package.
 | 
			
		||||
 | 
			
		||||
More examples are available [on the Gorilla
 | 
			
		||||
website](http://www.gorillatoolkit.org/pkg/sessions).
 | 
			
		||||
 | 
			
		||||
## Store Implementations
 | 
			
		||||
 | 
			
		||||
Other implementations of the `sessions.Store` interface:
 | 
			
		||||
 | 
			
		||||
* [github.com/starJammer/gorilla-sessions-arangodb](https://github.com/starJammer/gorilla-sessions-arangodb) - ArangoDB
 | 
			
		||||
* [github.com/yosssi/boltstore](https://github.com/yosssi/boltstore) - Bolt
 | 
			
		||||
* [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase
 | 
			
		||||
* [github.com/denizeren/dynamostore](https://github.com/denizeren/dynamostore) - Dynamodb on AWS
 | 
			
		||||
* [github.com/savaki/dynastore](https://github.com/savaki/dynastore) - DynamoDB on AWS (Official AWS library)
 | 
			
		||||
* [github.com/bradleypeabody/gorilla-sessions-memcache](https://github.com/bradleypeabody/gorilla-sessions-memcache) - Memcache
 | 
			
		||||
* [github.com/dsoprea/go-appengine-sessioncascade](https://github.com/dsoprea/go-appengine-sessioncascade) - Memcache/Datastore/Context in AppEngine
 | 
			
		||||
* [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB
 | 
			
		||||
* [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL
 | 
			
		||||
* [github.com/EnumApps/clustersqlstore](https://github.com/EnumApps/clustersqlstore) - MySQL Cluster
 | 
			
		||||
* [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL
 | 
			
		||||
* [github.com/boj/redistore](https://github.com/boj/redistore) - Redis
 | 
			
		||||
* [github.com/boj/rethinkstore](https://github.com/boj/rethinkstore) - RethinkDB
 | 
			
		||||
* [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak
 | 
			
		||||
* [github.com/michaeljs1990/sqlitestore](https://github.com/michaeljs1990/sqlitestore) - SQLite
 | 
			
		||||
* [github.com/wader/gormstore](https://github.com/wader/gormstore) - GORM (MySQL, PostgreSQL, SQLite)
 | 
			
		||||
* [github.com/gernest/qlstore](https://github.com/gernest/qlstore) - ql
 | 
			
		||||
 | 
			
		||||
## License
 | 
			
		||||
 | 
			
		||||
BSD licensed. See the LICENSE file for details.
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,198 @@
 | 
			
		|||
// Copyright 2012 The Gorilla Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Package sessions provides cookie and filesystem sessions and
 | 
			
		||||
infrastructure for custom session backends.
 | 
			
		||||
 | 
			
		||||
The key features are:
 | 
			
		||||
 | 
			
		||||
	* Simple API: use it as an easy way to set signed (and optionally
 | 
			
		||||
	  encrypted) cookies.
 | 
			
		||||
	* Built-in backends to store sessions in cookies or the filesystem.
 | 
			
		||||
	* Flash messages: session values that last until read.
 | 
			
		||||
	* Convenient way to switch session persistency (aka "remember me") and set
 | 
			
		||||
	  other attributes.
 | 
			
		||||
	* Mechanism to rotate authentication and encryption keys.
 | 
			
		||||
	* Multiple sessions per request, even using different backends.
 | 
			
		||||
	* Interfaces and infrastructure for custom session backends: sessions from
 | 
			
		||||
	  different stores can be retrieved and batch-saved using a common API.
 | 
			
		||||
 | 
			
		||||
Let's start with an example that shows the sessions API in a nutshell:
 | 
			
		||||
 | 
			
		||||
	import (
 | 
			
		||||
		"net/http"
 | 
			
		||||
		"github.com/gorilla/sessions"
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	var store = sessions.NewCookieStore([]byte("something-very-secret"))
 | 
			
		||||
 | 
			
		||||
	func MyHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		// Get a session. Get() always returns a session, even if empty.
 | 
			
		||||
		session, err := store.Get(r, "session-name")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Set some session values.
 | 
			
		||||
		session.Values["foo"] = "bar"
 | 
			
		||||
		session.Values[42] = 43
 | 
			
		||||
		// Save it before we write to the response/return from the handler.
 | 
			
		||||
		session.Save(r, w)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
First we initialize a session store calling NewCookieStore() and passing a
 | 
			
		||||
secret key used to authenticate the session. Inside the handler, we call
 | 
			
		||||
store.Get() to retrieve an existing session or a new one. Then we set some
 | 
			
		||||
session values in session.Values, which is a map[interface{}]interface{}.
 | 
			
		||||
And finally we call session.Save() to save the session in the response.
 | 
			
		||||
 | 
			
		||||
Note that in production code, we should check for errors when calling
 | 
			
		||||
session.Save(r, w), and either display an error message or otherwise handle it.
 | 
			
		||||
 | 
			
		||||
Save must be called before writing to the response, otherwise the session
 | 
			
		||||
cookie will not be sent to the client.
 | 
			
		||||
 | 
			
		||||
Important Note: If you aren't using gorilla/mux, you need to wrap your handlers
 | 
			
		||||
with context.ClearHandler as or else you will leak memory! An easy way to do this
 | 
			
		||||
is to wrap the top-level mux when calling http.ListenAndServe:
 | 
			
		||||
 | 
			
		||||
    http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux))
 | 
			
		||||
 | 
			
		||||
The ClearHandler function is provided by the gorilla/context package.
 | 
			
		||||
 | 
			
		||||
That's all you need to know for the basic usage. Let's take a look at other
 | 
			
		||||
options, starting with flash messages.
 | 
			
		||||
 | 
			
		||||
Flash messages are session values that last until read. The term appeared with
 | 
			
		||||
Ruby On Rails a few years back. When we request a flash message, it is removed
 | 
			
		||||
from the session. To add a flash, call session.AddFlash(), and to get all
 | 
			
		||||
flashes, call session.Flashes(). Here is an example:
 | 
			
		||||
 | 
			
		||||
	func MyHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		// Get a session.
 | 
			
		||||
		session, err := store.Get(r, "session-name")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Get the previously flashes, if any.
 | 
			
		||||
		if flashes := session.Flashes(); len(flashes) > 0 {
 | 
			
		||||
			// Use the flash values.
 | 
			
		||||
		} else {
 | 
			
		||||
			// Set a new flash.
 | 
			
		||||
			session.AddFlash("Hello, flash messages world!")
 | 
			
		||||
		}
 | 
			
		||||
		session.Save(r, w)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
Flash messages are useful to set information to be read after a redirection,
 | 
			
		||||
like after form submissions.
 | 
			
		||||
 | 
			
		||||
There may also be cases where you want to store a complex datatype within a
 | 
			
		||||
session, such as a struct. Sessions are serialised using the encoding/gob package,
 | 
			
		||||
so it is easy to register new datatypes for storage in sessions:
 | 
			
		||||
 | 
			
		||||
	import(
 | 
			
		||||
		"encoding/gob"
 | 
			
		||||
		"github.com/gorilla/sessions"
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	type Person struct {
 | 
			
		||||
		FirstName	string
 | 
			
		||||
		LastName 	string
 | 
			
		||||
		Email		string
 | 
			
		||||
		Age			int
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type M map[string]interface{}
 | 
			
		||||
 | 
			
		||||
	func init() {
 | 
			
		||||
 | 
			
		||||
		gob.Register(&Person{})
 | 
			
		||||
		gob.Register(&M{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
As it's not possible to pass a raw type as a parameter to a function, gob.Register()
 | 
			
		||||
relies on us passing it a value of the desired type. In the example above we've passed
 | 
			
		||||
it a pointer to a struct and a pointer to a custom type representing a
 | 
			
		||||
map[string]interface. (We could have passed non-pointer values if we wished.) This will
 | 
			
		||||
then allow us to serialise/deserialise values of those types to and from our sessions.
 | 
			
		||||
 | 
			
		||||
Note that because session values are stored in a map[string]interface{}, there's
 | 
			
		||||
a need to type-assert data when retrieving it. We'll use the Person struct we registered above:
 | 
			
		||||
 | 
			
		||||
	func MyHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		session, err := store.Get(r, "session-name")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Retrieve our struct and type-assert it
 | 
			
		||||
		val := session.Values["person"]
 | 
			
		||||
		var person = &Person{}
 | 
			
		||||
		if person, ok := val.(*Person); !ok {
 | 
			
		||||
			// Handle the case that it's not an expected type
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Now we can use our person object
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
By default, session cookies last for a month. This is probably too long for
 | 
			
		||||
some cases, but it is easy to change this and other attributes during
 | 
			
		||||
runtime. Sessions can be configured individually or the store can be
 | 
			
		||||
configured and then all sessions saved using it will use that configuration.
 | 
			
		||||
We access session.Options or store.Options to set a new configuration. The
 | 
			
		||||
fields are basically a subset of http.Cookie fields. Let's change the
 | 
			
		||||
maximum age of a session to one week:
 | 
			
		||||
 | 
			
		||||
	session.Options = &sessions.Options{
 | 
			
		||||
		Path:     "/",
 | 
			
		||||
		MaxAge:   86400 * 7,
 | 
			
		||||
		HttpOnly: true,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
Sometimes we may want to change authentication and/or encryption keys without
 | 
			
		||||
breaking existing sessions. The CookieStore supports key rotation, and to use
 | 
			
		||||
it you just need to set multiple authentication and encryption keys, in pairs,
 | 
			
		||||
to be tested in order:
 | 
			
		||||
 | 
			
		||||
	var store = sessions.NewCookieStore(
 | 
			
		||||
		[]byte("new-authentication-key"),
 | 
			
		||||
		[]byte("new-encryption-key"),
 | 
			
		||||
		[]byte("old-authentication-key"),
 | 
			
		||||
		[]byte("old-encryption-key"),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
New sessions will be saved using the first pair. Old sessions can still be
 | 
			
		||||
read because the first pair will fail, and the second will be tested. This
 | 
			
		||||
makes it easy to "rotate" secret keys and still be able to validate existing
 | 
			
		||||
sessions. Note: for all pairs the encryption key is optional; set it to nil
 | 
			
		||||
or omit it and and encryption won't be used.
 | 
			
		||||
 | 
			
		||||
Multiple sessions can be used in the same request, even with different
 | 
			
		||||
session backends. When this happens, calling Save() on each session
 | 
			
		||||
individually would be cumbersome, so we have a way to save all sessions
 | 
			
		||||
at once: it's sessions.Save(). Here's an example:
 | 
			
		||||
 | 
			
		||||
	var store = sessions.NewCookieStore([]byte("something-very-secret"))
 | 
			
		||||
 | 
			
		||||
	func MyHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		// Get a session and set a value.
 | 
			
		||||
		session1, _ := store.Get(r, "session-one")
 | 
			
		||||
		session1.Values["foo"] = "bar"
 | 
			
		||||
		// Get another session and set another value.
 | 
			
		||||
		session2, _ := store.Get(r, "session-two")
 | 
			
		||||
		session2.Values[42] = 43
 | 
			
		||||
		// Save all sessions.
 | 
			
		||||
		sessions.Save(r, w)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
This is possible because when we call Get() from a session store, it adds the
 | 
			
		||||
session to a common registry. Save() uses it to save all registered sessions.
 | 
			
		||||
*/
 | 
			
		||||
package sessions
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,102 @@
 | 
			
		|||
// This file contains code adapted from the Go standard library
 | 
			
		||||
// https://github.com/golang/go/blob/39ad0fd0789872f9469167be7fe9578625ff246e/src/net/http/lex.go
 | 
			
		||||
 | 
			
		||||
package sessions
 | 
			
		||||
 | 
			
		||||
import "strings"
 | 
			
		||||
 | 
			
		||||
var isTokenTable = [127]bool{
 | 
			
		||||
	'!':  true,
 | 
			
		||||
	'#':  true,
 | 
			
		||||
	'$':  true,
 | 
			
		||||
	'%':  true,
 | 
			
		||||
	'&':  true,
 | 
			
		||||
	'\'': true,
 | 
			
		||||
	'*':  true,
 | 
			
		||||
	'+':  true,
 | 
			
		||||
	'-':  true,
 | 
			
		||||
	'.':  true,
 | 
			
		||||
	'0':  true,
 | 
			
		||||
	'1':  true,
 | 
			
		||||
	'2':  true,
 | 
			
		||||
	'3':  true,
 | 
			
		||||
	'4':  true,
 | 
			
		||||
	'5':  true,
 | 
			
		||||
	'6':  true,
 | 
			
		||||
	'7':  true,
 | 
			
		||||
	'8':  true,
 | 
			
		||||
	'9':  true,
 | 
			
		||||
	'A':  true,
 | 
			
		||||
	'B':  true,
 | 
			
		||||
	'C':  true,
 | 
			
		||||
	'D':  true,
 | 
			
		||||
	'E':  true,
 | 
			
		||||
	'F':  true,
 | 
			
		||||
	'G':  true,
 | 
			
		||||
	'H':  true,
 | 
			
		||||
	'I':  true,
 | 
			
		||||
	'J':  true,
 | 
			
		||||
	'K':  true,
 | 
			
		||||
	'L':  true,
 | 
			
		||||
	'M':  true,
 | 
			
		||||
	'N':  true,
 | 
			
		||||
	'O':  true,
 | 
			
		||||
	'P':  true,
 | 
			
		||||
	'Q':  true,
 | 
			
		||||
	'R':  true,
 | 
			
		||||
	'S':  true,
 | 
			
		||||
	'T':  true,
 | 
			
		||||
	'U':  true,
 | 
			
		||||
	'W':  true,
 | 
			
		||||
	'V':  true,
 | 
			
		||||
	'X':  true,
 | 
			
		||||
	'Y':  true,
 | 
			
		||||
	'Z':  true,
 | 
			
		||||
	'^':  true,
 | 
			
		||||
	'_':  true,
 | 
			
		||||
	'`':  true,
 | 
			
		||||
	'a':  true,
 | 
			
		||||
	'b':  true,
 | 
			
		||||
	'c':  true,
 | 
			
		||||
	'd':  true,
 | 
			
		||||
	'e':  true,
 | 
			
		||||
	'f':  true,
 | 
			
		||||
	'g':  true,
 | 
			
		||||
	'h':  true,
 | 
			
		||||
	'i':  true,
 | 
			
		||||
	'j':  true,
 | 
			
		||||
	'k':  true,
 | 
			
		||||
	'l':  true,
 | 
			
		||||
	'm':  true,
 | 
			
		||||
	'n':  true,
 | 
			
		||||
	'o':  true,
 | 
			
		||||
	'p':  true,
 | 
			
		||||
	'q':  true,
 | 
			
		||||
	'r':  true,
 | 
			
		||||
	's':  true,
 | 
			
		||||
	't':  true,
 | 
			
		||||
	'u':  true,
 | 
			
		||||
	'v':  true,
 | 
			
		||||
	'w':  true,
 | 
			
		||||
	'x':  true,
 | 
			
		||||
	'y':  true,
 | 
			
		||||
	'z':  true,
 | 
			
		||||
	'|':  true,
 | 
			
		||||
	'~':  true,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isToken(r rune) bool {
 | 
			
		||||
	i := int(r)
 | 
			
		||||
	return i < len(isTokenTable) && isTokenTable[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isNotToken(r rune) bool {
 | 
			
		||||
	return !isToken(r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isCookieNameValid(raw string) bool {
 | 
			
		||||
	if raw == "" {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return strings.IndexFunc(raw, isNotToken) < 0
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,241 @@
 | 
			
		|||
// Copyright 2012 The Gorilla Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package sessions
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/gob"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Default flashes key.
 | 
			
		||||
const flashesKey = "_flash"
 | 
			
		||||
 | 
			
		||||
// Options --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// Options stores configuration for a session or session store.
 | 
			
		||||
//
 | 
			
		||||
// Fields are a subset of http.Cookie fields.
 | 
			
		||||
type Options struct {
 | 
			
		||||
	Path   string
 | 
			
		||||
	Domain string
 | 
			
		||||
	// MaxAge=0 means no 'Max-Age' attribute specified.
 | 
			
		||||
	// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'.
 | 
			
		||||
	// MaxAge>0 means Max-Age attribute present and given in seconds.
 | 
			
		||||
	MaxAge   int
 | 
			
		||||
	Secure   bool
 | 
			
		||||
	HttpOnly bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Session --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// NewSession is called by session stores to create a new session instance.
 | 
			
		||||
func NewSession(store Store, name string) *Session {
 | 
			
		||||
	return &Session{
 | 
			
		||||
		Values: make(map[interface{}]interface{}),
 | 
			
		||||
		store:  store,
 | 
			
		||||
		name:   name,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Session stores the values and optional configuration for a session.
 | 
			
		||||
type Session struct {
 | 
			
		||||
	// The ID of the session, generated by stores. It should not be used for
 | 
			
		||||
	// user data.
 | 
			
		||||
	ID string
 | 
			
		||||
	// Values contains the user-data for the session.
 | 
			
		||||
	Values  map[interface{}]interface{}
 | 
			
		||||
	Options *Options
 | 
			
		||||
	IsNew   bool
 | 
			
		||||
	store   Store
 | 
			
		||||
	name    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Flashes returns a slice of flash messages from the session.
 | 
			
		||||
//
 | 
			
		||||
// A single variadic argument is accepted, and it is optional: it defines
 | 
			
		||||
// the flash key. If not defined "_flash" is used by default.
 | 
			
		||||
func (s *Session) Flashes(vars ...string) []interface{} {
 | 
			
		||||
	var flashes []interface{}
 | 
			
		||||
	key := flashesKey
 | 
			
		||||
	if len(vars) > 0 {
 | 
			
		||||
		key = vars[0]
 | 
			
		||||
	}
 | 
			
		||||
	if v, ok := s.Values[key]; ok {
 | 
			
		||||
		// Drop the flashes and return it.
 | 
			
		||||
		delete(s.Values, key)
 | 
			
		||||
		flashes = v.([]interface{})
 | 
			
		||||
	}
 | 
			
		||||
	return flashes
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddFlash adds a flash message to the session.
 | 
			
		||||
//
 | 
			
		||||
// A single variadic argument is accepted, and it is optional: it defines
 | 
			
		||||
// the flash key. If not defined "_flash" is used by default.
 | 
			
		||||
func (s *Session) AddFlash(value interface{}, vars ...string) {
 | 
			
		||||
	key := flashesKey
 | 
			
		||||
	if len(vars) > 0 {
 | 
			
		||||
		key = vars[0]
 | 
			
		||||
	}
 | 
			
		||||
	var flashes []interface{}
 | 
			
		||||
	if v, ok := s.Values[key]; ok {
 | 
			
		||||
		flashes = v.([]interface{})
 | 
			
		||||
	}
 | 
			
		||||
	s.Values[key] = append(flashes, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Save is a convenience method to save this session. It is the same as calling
 | 
			
		||||
// store.Save(request, response, session). You should call Save before writing to
 | 
			
		||||
// the response or returning from the handler.
 | 
			
		||||
func (s *Session) Save(r *http.Request, w http.ResponseWriter) error {
 | 
			
		||||
	return s.store.Save(r, w, s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name returns the name used to register the session.
 | 
			
		||||
func (s *Session) Name() string {
 | 
			
		||||
	return s.name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Store returns the session store used to register the session.
 | 
			
		||||
func (s *Session) Store() Store {
 | 
			
		||||
	return s.store
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Registry -------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// sessionInfo stores a session tracked by the registry.
 | 
			
		||||
type sessionInfo struct {
 | 
			
		||||
	s *Session
 | 
			
		||||
	e error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// contextKey is the type used to store the registry in the context.
 | 
			
		||||
type contextKey int
 | 
			
		||||
 | 
			
		||||
// registryKey is the key used to store the registry in the context.
 | 
			
		||||
const registryKey contextKey = 0
 | 
			
		||||
 | 
			
		||||
// GetRegistry returns a registry instance for the current request.
 | 
			
		||||
func GetRegistry(r *http.Request) *Registry {
 | 
			
		||||
	registry := context.Get(r, registryKey)
 | 
			
		||||
	if registry != nil {
 | 
			
		||||
		return registry.(*Registry)
 | 
			
		||||
	}
 | 
			
		||||
	newRegistry := &Registry{
 | 
			
		||||
		request:  r,
 | 
			
		||||
		sessions: make(map[string]sessionInfo),
 | 
			
		||||
	}
 | 
			
		||||
	context.Set(r, registryKey, newRegistry)
 | 
			
		||||
	return newRegistry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Registry stores sessions used during a request.
 | 
			
		||||
type Registry struct {
 | 
			
		||||
	request  *http.Request
 | 
			
		||||
	sessions map[string]sessionInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get registers and returns a session for the given name and session store.
 | 
			
		||||
//
 | 
			
		||||
// It returns a new session if there are no sessions registered for the name.
 | 
			
		||||
func (s *Registry) Get(store Store, name string) (session *Session, err error) {
 | 
			
		||||
	if !isCookieNameValid(name) {
 | 
			
		||||
		return nil, fmt.Errorf("sessions: invalid character in cookie name: %s", name)
 | 
			
		||||
	}
 | 
			
		||||
	if info, ok := s.sessions[name]; ok {
 | 
			
		||||
		session, err = info.s, info.e
 | 
			
		||||
	} else {
 | 
			
		||||
		session, err = store.New(s.request, name)
 | 
			
		||||
		session.name = name
 | 
			
		||||
		s.sessions[name] = sessionInfo{s: session, e: err}
 | 
			
		||||
	}
 | 
			
		||||
	session.store = store
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Save saves all sessions registered for the current request.
 | 
			
		||||
func (s *Registry) Save(w http.ResponseWriter) error {
 | 
			
		||||
	var errMulti MultiError
 | 
			
		||||
	for name, info := range s.sessions {
 | 
			
		||||
		session := info.s
 | 
			
		||||
		if session.store == nil {
 | 
			
		||||
			errMulti = append(errMulti, fmt.Errorf(
 | 
			
		||||
				"sessions: missing store for session %q", name))
 | 
			
		||||
		} else if err := session.store.Save(s.request, w, session); err != nil {
 | 
			
		||||
			errMulti = append(errMulti, fmt.Errorf(
 | 
			
		||||
				"sessions: error saving session %q -- %v", name, err))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if errMulti != nil {
 | 
			
		||||
		return errMulti
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helpers --------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	gob.Register([]interface{}{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Save saves all sessions used during the current request.
 | 
			
		||||
func Save(r *http.Request, w http.ResponseWriter) error {
 | 
			
		||||
	return GetRegistry(r).Save(w)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewCookie returns an http.Cookie with the options set. It also sets
 | 
			
		||||
// the Expires field calculated based on the MaxAge value, for Internet
 | 
			
		||||
// Explorer compatibility.
 | 
			
		||||
func NewCookie(name, value string, options *Options) *http.Cookie {
 | 
			
		||||
	cookie := &http.Cookie{
 | 
			
		||||
		Name:     name,
 | 
			
		||||
		Value:    value,
 | 
			
		||||
		Path:     options.Path,
 | 
			
		||||
		Domain:   options.Domain,
 | 
			
		||||
		MaxAge:   options.MaxAge,
 | 
			
		||||
		Secure:   options.Secure,
 | 
			
		||||
		HttpOnly: options.HttpOnly,
 | 
			
		||||
	}
 | 
			
		||||
	if options.MaxAge > 0 {
 | 
			
		||||
		d := time.Duration(options.MaxAge) * time.Second
 | 
			
		||||
		cookie.Expires = time.Now().Add(d)
 | 
			
		||||
	} else if options.MaxAge < 0 {
 | 
			
		||||
		// Set it to the past to expire now.
 | 
			
		||||
		cookie.Expires = time.Unix(1, 0)
 | 
			
		||||
	}
 | 
			
		||||
	return cookie
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error ----------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// MultiError stores multiple errors.
 | 
			
		||||
//
 | 
			
		||||
// Borrowed from the App Engine SDK.
 | 
			
		||||
type MultiError []error
 | 
			
		||||
 | 
			
		||||
func (m MultiError) Error() string {
 | 
			
		||||
	s, n := "", 0
 | 
			
		||||
	for _, e := range m {
 | 
			
		||||
		if e != nil {
 | 
			
		||||
			if n == 0 {
 | 
			
		||||
				s = e.Error()
 | 
			
		||||
			}
 | 
			
		||||
			n++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	switch n {
 | 
			
		||||
	case 0:
 | 
			
		||||
		return "(0 errors)"
 | 
			
		||||
	case 1:
 | 
			
		||||
		return s
 | 
			
		||||
	case 2:
 | 
			
		||||
		return s + " (and 1 other error)"
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s (and %d other errors)", s, n-1)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,295 @@
 | 
			
		|||
// Copyright 2012 The Gorilla Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package sessions
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/base32"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/securecookie"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Store is an interface for custom session stores.
 | 
			
		||||
//
 | 
			
		||||
// See CookieStore and FilesystemStore for examples.
 | 
			
		||||
type Store interface {
 | 
			
		||||
	// Get should return a cached session.
 | 
			
		||||
	Get(r *http.Request, name string) (*Session, error)
 | 
			
		||||
 | 
			
		||||
	// New should create and return a new session.
 | 
			
		||||
	//
 | 
			
		||||
	// Note that New should never return a nil session, even in the case of
 | 
			
		||||
	// an error if using the Registry infrastructure to cache the session.
 | 
			
		||||
	New(r *http.Request, name string) (*Session, error)
 | 
			
		||||
 | 
			
		||||
	// Save should persist session to the underlying store implementation.
 | 
			
		||||
	Save(r *http.Request, w http.ResponseWriter, s *Session) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CookieStore ----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// NewCookieStore returns a new CookieStore.
 | 
			
		||||
//
 | 
			
		||||
// Keys are defined in pairs to allow key rotation, but the common case is
 | 
			
		||||
// to set a single authentication key and optionally an encryption key.
 | 
			
		||||
//
 | 
			
		||||
// The first key in a pair is used for authentication and the second for
 | 
			
		||||
// encryption. The encryption key can be set to nil or omitted in the last
 | 
			
		||||
// pair, but the authentication key is required in all pairs.
 | 
			
		||||
//
 | 
			
		||||
// It is recommended to use an authentication key with 32 or 64 bytes.
 | 
			
		||||
// The encryption key, if set, must be either 16, 24, or 32 bytes to select
 | 
			
		||||
// AES-128, AES-192, or AES-256 modes.
 | 
			
		||||
//
 | 
			
		||||
// Use the convenience function securecookie.GenerateRandomKey() to create
 | 
			
		||||
// strong keys.
 | 
			
		||||
func NewCookieStore(keyPairs ...[]byte) *CookieStore {
 | 
			
		||||
	cs := &CookieStore{
 | 
			
		||||
		Codecs: securecookie.CodecsFromPairs(keyPairs...),
 | 
			
		||||
		Options: &Options{
 | 
			
		||||
			Path:   "/",
 | 
			
		||||
			MaxAge: 86400 * 30,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cs.MaxAge(cs.Options.MaxAge)
 | 
			
		||||
	return cs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CookieStore stores sessions using secure cookies.
 | 
			
		||||
type CookieStore struct {
 | 
			
		||||
	Codecs  []securecookie.Codec
 | 
			
		||||
	Options *Options // default configuration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get returns a session for the given name after adding it to the registry.
 | 
			
		||||
//
 | 
			
		||||
// It returns a new session if the sessions doesn't exist. Access IsNew on
 | 
			
		||||
// the session to check if it is an existing session or a new one.
 | 
			
		||||
//
 | 
			
		||||
// It returns a new session and an error if the session exists but could
 | 
			
		||||
// not be decoded.
 | 
			
		||||
func (s *CookieStore) Get(r *http.Request, name string) (*Session, error) {
 | 
			
		||||
	return GetRegistry(r).Get(s, name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New returns a session for the given name without adding it to the registry.
 | 
			
		||||
//
 | 
			
		||||
// The difference between New() and Get() is that calling New() twice will
 | 
			
		||||
// decode the session data twice, while Get() registers and reuses the same
 | 
			
		||||
// decoded session after the first call.
 | 
			
		||||
func (s *CookieStore) New(r *http.Request, name string) (*Session, error) {
 | 
			
		||||
	session := NewSession(s, name)
 | 
			
		||||
	opts := *s.Options
 | 
			
		||||
	session.Options = &opts
 | 
			
		||||
	session.IsNew = true
 | 
			
		||||
	var err error
 | 
			
		||||
	if c, errCookie := r.Cookie(name); errCookie == nil {
 | 
			
		||||
		err = securecookie.DecodeMulti(name, c.Value, &session.Values,
 | 
			
		||||
			s.Codecs...)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			session.IsNew = false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return session, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Save adds a single session to the response.
 | 
			
		||||
func (s *CookieStore) Save(r *http.Request, w http.ResponseWriter,
 | 
			
		||||
	session *Session) error {
 | 
			
		||||
	encoded, err := securecookie.EncodeMulti(session.Name(), session.Values,
 | 
			
		||||
		s.Codecs...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	http.SetCookie(w, NewCookie(session.Name(), encoded, session.Options))
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MaxAge sets the maximum age for the store and the underlying cookie
 | 
			
		||||
// implementation. Individual sessions can be deleted by setting Options.MaxAge
 | 
			
		||||
// = -1 for that session.
 | 
			
		||||
func (s *CookieStore) MaxAge(age int) {
 | 
			
		||||
	s.Options.MaxAge = age
 | 
			
		||||
 | 
			
		||||
	// Set the maxAge for each securecookie instance.
 | 
			
		||||
	for _, codec := range s.Codecs {
 | 
			
		||||
		if sc, ok := codec.(*securecookie.SecureCookie); ok {
 | 
			
		||||
			sc.MaxAge(age)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FilesystemStore ------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
var fileMutex sync.RWMutex
 | 
			
		||||
 | 
			
		||||
// NewFilesystemStore returns a new FilesystemStore.
 | 
			
		||||
//
 | 
			
		||||
// The path argument is the directory where sessions will be saved. If empty
 | 
			
		||||
// it will use os.TempDir().
 | 
			
		||||
//
 | 
			
		||||
// See NewCookieStore() for a description of the other parameters.
 | 
			
		||||
func NewFilesystemStore(path string, keyPairs ...[]byte) *FilesystemStore {
 | 
			
		||||
	if path == "" {
 | 
			
		||||
		path = os.TempDir()
 | 
			
		||||
	}
 | 
			
		||||
	fs := &FilesystemStore{
 | 
			
		||||
		Codecs: securecookie.CodecsFromPairs(keyPairs...),
 | 
			
		||||
		Options: &Options{
 | 
			
		||||
			Path:   "/",
 | 
			
		||||
			MaxAge: 86400 * 30,
 | 
			
		||||
		},
 | 
			
		||||
		path: path,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fs.MaxAge(fs.Options.MaxAge)
 | 
			
		||||
	return fs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FilesystemStore stores sessions in the filesystem.
 | 
			
		||||
//
 | 
			
		||||
// It also serves as a reference for custom stores.
 | 
			
		||||
//
 | 
			
		||||
// This store is still experimental and not well tested. Feedback is welcome.
 | 
			
		||||
type FilesystemStore struct {
 | 
			
		||||
	Codecs  []securecookie.Codec
 | 
			
		||||
	Options *Options // default configuration
 | 
			
		||||
	path    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MaxLength restricts the maximum length of new sessions to l.
 | 
			
		||||
// If l is 0 there is no limit to the size of a session, use with caution.
 | 
			
		||||
// The default for a new FilesystemStore is 4096.
 | 
			
		||||
func (s *FilesystemStore) MaxLength(l int) {
 | 
			
		||||
	for _, c := range s.Codecs {
 | 
			
		||||
		if codec, ok := c.(*securecookie.SecureCookie); ok {
 | 
			
		||||
			codec.MaxLength(l)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get returns a session for the given name after adding it to the registry.
 | 
			
		||||
//
 | 
			
		||||
// See CookieStore.Get().
 | 
			
		||||
func (s *FilesystemStore) Get(r *http.Request, name string) (*Session, error) {
 | 
			
		||||
	return GetRegistry(r).Get(s, name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New returns a session for the given name without adding it to the registry.
 | 
			
		||||
//
 | 
			
		||||
// See CookieStore.New().
 | 
			
		||||
func (s *FilesystemStore) New(r *http.Request, name string) (*Session, error) {
 | 
			
		||||
	session := NewSession(s, name)
 | 
			
		||||
	opts := *s.Options
 | 
			
		||||
	session.Options = &opts
 | 
			
		||||
	session.IsNew = true
 | 
			
		||||
	var err error
 | 
			
		||||
	if c, errCookie := r.Cookie(name); errCookie == nil {
 | 
			
		||||
		err = securecookie.DecodeMulti(name, c.Value, &session.ID, s.Codecs...)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			err = s.load(session)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				session.IsNew = false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return session, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Save adds a single session to the response.
 | 
			
		||||
//
 | 
			
		||||
// If the Options.MaxAge of the session is <= 0 then the session file will be
 | 
			
		||||
// deleted from the store path. With this process it enforces the properly
 | 
			
		||||
// session cookie handling so no need to trust in the cookie management in the
 | 
			
		||||
// web browser.
 | 
			
		||||
func (s *FilesystemStore) Save(r *http.Request, w http.ResponseWriter,
 | 
			
		||||
	session *Session) error {
 | 
			
		||||
	// Delete if max-age is <= 0
 | 
			
		||||
	if session.Options.MaxAge <= 0 {
 | 
			
		||||
		if err := s.erase(session); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		http.SetCookie(w, NewCookie(session.Name(), "", session.Options))
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if session.ID == "" {
 | 
			
		||||
		// Because the ID is used in the filename, encode it to
 | 
			
		||||
		// use alphanumeric characters only.
 | 
			
		||||
		session.ID = strings.TrimRight(
 | 
			
		||||
			base32.StdEncoding.EncodeToString(
 | 
			
		||||
				securecookie.GenerateRandomKey(32)), "=")
 | 
			
		||||
	}
 | 
			
		||||
	if err := s.save(session); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	encoded, err := securecookie.EncodeMulti(session.Name(), session.ID,
 | 
			
		||||
		s.Codecs...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	http.SetCookie(w, NewCookie(session.Name(), encoded, session.Options))
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MaxAge sets the maximum age for the store and the underlying cookie
 | 
			
		||||
// implementation. Individual sessions can be deleted by setting Options.MaxAge
 | 
			
		||||
// = -1 for that session.
 | 
			
		||||
func (s *FilesystemStore) MaxAge(age int) {
 | 
			
		||||
	s.Options.MaxAge = age
 | 
			
		||||
 | 
			
		||||
	// Set the maxAge for each securecookie instance.
 | 
			
		||||
	for _, codec := range s.Codecs {
 | 
			
		||||
		if sc, ok := codec.(*securecookie.SecureCookie); ok {
 | 
			
		||||
			sc.MaxAge(age)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// save writes encoded session.Values to a file.
 | 
			
		||||
func (s *FilesystemStore) save(session *Session) error {
 | 
			
		||||
	encoded, err := securecookie.EncodeMulti(session.Name(), session.Values,
 | 
			
		||||
		s.Codecs...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	filename := filepath.Join(s.path, "session_"+session.ID)
 | 
			
		||||
	fileMutex.Lock()
 | 
			
		||||
	defer fileMutex.Unlock()
 | 
			
		||||
	return ioutil.WriteFile(filename, []byte(encoded), 0600)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// load reads a file and decodes its content into session.Values.
 | 
			
		||||
func (s *FilesystemStore) load(session *Session) error {
 | 
			
		||||
	filename := filepath.Join(s.path, "session_"+session.ID)
 | 
			
		||||
	fileMutex.RLock()
 | 
			
		||||
	defer fileMutex.RUnlock()
 | 
			
		||||
	fdata, err := ioutil.ReadFile(filename)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err = securecookie.DecodeMulti(session.Name(), string(fdata),
 | 
			
		||||
		&session.Values, s.Codecs...); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// delete session file
 | 
			
		||||
func (s *FilesystemStore) erase(session *Session) error {
 | 
			
		||||
	filename := filepath.Join(s.path, "session_"+session.ID)
 | 
			
		||||
 | 
			
		||||
	fileMutex.RLock()
 | 
			
		||||
	defer fileMutex.RUnlock()
 | 
			
		||||
 | 
			
		||||
	err := os.Remove(filename)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
# This is the official list of Gorilla WebSocket authors for copyright
 | 
			
		||||
# purposes.
 | 
			
		||||
#
 | 
			
		||||
# Please keep the list sorted.
 | 
			
		||||
 | 
			
		||||
Gary Burd <gary@beagledreams.com>
 | 
			
		||||
Joachim Bauch <mail@joachim-bauch.de>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are met:
 | 
			
		||||
 | 
			
		||||
  Redistributions of source code must retain the above copyright notice, this
 | 
			
		||||
  list of conditions and the following disclaimer.
 | 
			
		||||
 | 
			
		||||
  Redistributions in binary form must reproduce the above copyright notice,
 | 
			
		||||
  this list of conditions and the following disclaimer in the documentation
 | 
			
		||||
  and/or other materials provided with the distribution.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 | 
			
		||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 | 
			
		||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 | 
			
		||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 | 
			
		||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
			
		||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 | 
			
		||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 | 
			
		||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 | 
			
		||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
			
		||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,64 @@
 | 
			
		|||
# Gorilla WebSocket
 | 
			
		||||
 | 
			
		||||
Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
 | 
			
		||||
[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.
 | 
			
		||||
 | 
			
		||||
[](https://travis-ci.org/gorilla/websocket)
 | 
			
		||||
[](https://godoc.org/github.com/gorilla/websocket)
 | 
			
		||||
 | 
			
		||||
### Documentation
 | 
			
		||||
 | 
			
		||||
* [API Reference](http://godoc.org/github.com/gorilla/websocket)
 | 
			
		||||
* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat)
 | 
			
		||||
* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command)
 | 
			
		||||
* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo)
 | 
			
		||||
* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch)
 | 
			
		||||
 | 
			
		||||
### Status
 | 
			
		||||
 | 
			
		||||
The Gorilla WebSocket package provides a complete and tested implementation of
 | 
			
		||||
the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The
 | 
			
		||||
package API is stable.
 | 
			
		||||
 | 
			
		||||
### Installation
 | 
			
		||||
 | 
			
		||||
    go get github.com/gorilla/websocket
 | 
			
		||||
 | 
			
		||||
### Protocol Compliance
 | 
			
		||||
 | 
			
		||||
The Gorilla WebSocket package passes the server tests in the [Autobahn Test
 | 
			
		||||
Suite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn
 | 
			
		||||
subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn).
 | 
			
		||||
 | 
			
		||||
### Gorilla WebSocket compared with other packages
 | 
			
		||||
 | 
			
		||||
<table>
 | 
			
		||||
<tr>
 | 
			
		||||
<th></th>
 | 
			
		||||
<th><a href="http://godoc.org/github.com/gorilla/websocket">github.com/gorilla</a></th>
 | 
			
		||||
<th><a href="http://godoc.org/golang.org/x/net/websocket">golang.org/x/net</a></th>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr>
 | 
			
		||||
<tr><td colspan="3"><a href="http://tools.ietf.org/html/rfc6455">RFC 6455</a> Features</td></tr>
 | 
			
		||||
<tr><td>Passes <a href="http://autobahn.ws/testsuite/">Autobahn Test Suite</a></td><td><a href="https://github.com/gorilla/websocket/tree/master/examples/autobahn">Yes</a></td><td>No</td></tr>
 | 
			
		||||
<tr><td>Receive <a href="https://tools.ietf.org/html/rfc6455#section-5.4">fragmented</a> message<td>Yes</td><td><a href="https://code.google.com/p/go/issues/detail?id=7632">No</a>, see note 1</td></tr>
 | 
			
		||||
<tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.1">close</a> message</td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td><a href="https://code.google.com/p/go/issues/detail?id=4588">No</a></td></tr>
 | 
			
		||||
<tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.2">pings</a> and receive <a href="https://tools.ietf.org/html/rfc6455#section-5.5.3">pongs</a></td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td>No</td></tr>
 | 
			
		||||
<tr><td>Get the <a href="https://tools.ietf.org/html/rfc6455#section-5.6">type</a> of a received data message</td><td>Yes</td><td>Yes, see note 2</td></tr>
 | 
			
		||||
<tr><td colspan="3">Other Features</tr></td>
 | 
			
		||||
<tr><td><a href="https://tools.ietf.org/html/rfc7692">Compression Extensions</a></td><td>Experimental</td><td>No</td></tr>
 | 
			
		||||
<tr><td>Read message using io.Reader</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextReader">Yes</a></td><td>No, see note 3</td></tr>
 | 
			
		||||
<tr><td>Write message using io.WriteCloser</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextWriter">Yes</a></td><td>No, see note 3</td></tr>
 | 
			
		||||
</table>
 | 
			
		||||
 | 
			
		||||
Notes: 
 | 
			
		||||
 | 
			
		||||
1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html).
 | 
			
		||||
2. The application can get the type of a received data message by implementing
 | 
			
		||||
   a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal)
 | 
			
		||||
   function.
 | 
			
		||||
3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries.
 | 
			
		||||
  Read returns when the input buffer is full or a frame boundary is
 | 
			
		||||
  encountered. Each call to Write sends a single frame message. The Gorilla
 | 
			
		||||
  io.Reader and io.WriteCloser operate on a single WebSocket message.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,392 @@
 | 
			
		|||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package websocket
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ErrBadHandshake is returned when the server response to opening handshake is
 | 
			
		||||
// invalid.
 | 
			
		||||
var ErrBadHandshake = errors.New("websocket: bad handshake")
 | 
			
		||||
 | 
			
		||||
var errInvalidCompression = errors.New("websocket: invalid compression negotiation")
 | 
			
		||||
 | 
			
		||||
// NewClient creates a new client connection using the given net connection.
 | 
			
		||||
// The URL u specifies the host and request URI. Use requestHeader to specify
 | 
			
		||||
// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies
 | 
			
		||||
// (Cookie). Use the response.Header to get the selected subprotocol
 | 
			
		||||
// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
 | 
			
		||||
//
 | 
			
		||||
// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
 | 
			
		||||
// non-nil *http.Response so that callers can handle redirects, authentication,
 | 
			
		||||
// etc.
 | 
			
		||||
//
 | 
			
		||||
// Deprecated: Use Dialer instead.
 | 
			
		||||
func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) {
 | 
			
		||||
	d := Dialer{
 | 
			
		||||
		ReadBufferSize:  readBufSize,
 | 
			
		||||
		WriteBufferSize: writeBufSize,
 | 
			
		||||
		NetDial: func(net, addr string) (net.Conn, error) {
 | 
			
		||||
			return netConn, nil
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	return d.Dial(u.String(), requestHeader)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A Dialer contains options for connecting to WebSocket server.
 | 
			
		||||
type Dialer struct {
 | 
			
		||||
	// NetDial specifies the dial function for creating TCP connections. If
 | 
			
		||||
	// NetDial is nil, net.Dial is used.
 | 
			
		||||
	NetDial func(network, addr string) (net.Conn, error)
 | 
			
		||||
 | 
			
		||||
	// Proxy specifies a function to return a proxy for a given
 | 
			
		||||
	// Request. If the function returns a non-nil error, the
 | 
			
		||||
	// request is aborted with the provided error.
 | 
			
		||||
	// If Proxy is nil or returns a nil *URL, no proxy is used.
 | 
			
		||||
	Proxy func(*http.Request) (*url.URL, error)
 | 
			
		||||
 | 
			
		||||
	// TLSClientConfig specifies the TLS configuration to use with tls.Client.
 | 
			
		||||
	// If nil, the default configuration is used.
 | 
			
		||||
	TLSClientConfig *tls.Config
 | 
			
		||||
 | 
			
		||||
	// HandshakeTimeout specifies the duration for the handshake to complete.
 | 
			
		||||
	HandshakeTimeout time.Duration
 | 
			
		||||
 | 
			
		||||
	// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer
 | 
			
		||||
	// size is zero, then a useful default size is used. The I/O buffer sizes
 | 
			
		||||
	// do not limit the size of the messages that can be sent or received.
 | 
			
		||||
	ReadBufferSize, WriteBufferSize int
 | 
			
		||||
 | 
			
		||||
	// Subprotocols specifies the client's requested subprotocols.
 | 
			
		||||
	Subprotocols []string
 | 
			
		||||
 | 
			
		||||
	// EnableCompression specifies if the client should attempt to negotiate
 | 
			
		||||
	// per message compression (RFC 7692). Setting this value to true does not
 | 
			
		||||
	// guarantee that compression will be supported. Currently only "no context
 | 
			
		||||
	// takeover" modes are supported.
 | 
			
		||||
	EnableCompression bool
 | 
			
		||||
 | 
			
		||||
	// Jar specifies the cookie jar.
 | 
			
		||||
	// If Jar is nil, cookies are not sent in requests and ignored
 | 
			
		||||
	// in responses.
 | 
			
		||||
	Jar http.CookieJar
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var errMalformedURL = errors.New("malformed ws or wss URL")
 | 
			
		||||
 | 
			
		||||
// parseURL parses the URL.
 | 
			
		||||
//
 | 
			
		||||
// This function is a replacement for the standard library url.Parse function.
 | 
			
		||||
// In Go 1.4 and earlier, url.Parse loses information from the path.
 | 
			
		||||
func parseURL(s string) (*url.URL, error) {
 | 
			
		||||
	// From the RFC:
 | 
			
		||||
	//
 | 
			
		||||
	// ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
 | 
			
		||||
	// wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
 | 
			
		||||
	var u url.URL
 | 
			
		||||
	switch {
 | 
			
		||||
	case strings.HasPrefix(s, "ws://"):
 | 
			
		||||
		u.Scheme = "ws"
 | 
			
		||||
		s = s[len("ws://"):]
 | 
			
		||||
	case strings.HasPrefix(s, "wss://"):
 | 
			
		||||
		u.Scheme = "wss"
 | 
			
		||||
		s = s[len("wss://"):]
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, errMalformedURL
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if i := strings.Index(s, "?"); i >= 0 {
 | 
			
		||||
		u.RawQuery = s[i+1:]
 | 
			
		||||
		s = s[:i]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if i := strings.Index(s, "/"); i >= 0 {
 | 
			
		||||
		u.Opaque = s[i:]
 | 
			
		||||
		s = s[:i]
 | 
			
		||||
	} else {
 | 
			
		||||
		u.Opaque = "/"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	u.Host = s
 | 
			
		||||
 | 
			
		||||
	if strings.Contains(u.Host, "@") {
 | 
			
		||||
		// Don't bother parsing user information because user information is
 | 
			
		||||
		// not allowed in websocket URIs.
 | 
			
		||||
		return nil, errMalformedURL
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &u, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
 | 
			
		||||
	hostPort = u.Host
 | 
			
		||||
	hostNoPort = u.Host
 | 
			
		||||
	if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") {
 | 
			
		||||
		hostNoPort = hostNoPort[:i]
 | 
			
		||||
	} else {
 | 
			
		||||
		switch u.Scheme {
 | 
			
		||||
		case "wss":
 | 
			
		||||
			hostPort += ":443"
 | 
			
		||||
		case "https":
 | 
			
		||||
			hostPort += ":443"
 | 
			
		||||
		default:
 | 
			
		||||
			hostPort += ":80"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return hostPort, hostNoPort
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DefaultDialer is a dialer with all fields set to the default zero values.
 | 
			
		||||
var DefaultDialer = &Dialer{
 | 
			
		||||
	Proxy: http.ProxyFromEnvironment,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Dial creates a new client connection. Use requestHeader to specify the
 | 
			
		||||
// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie).
 | 
			
		||||
// Use the response.Header to get the selected subprotocol
 | 
			
		||||
// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
 | 
			
		||||
//
 | 
			
		||||
// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
 | 
			
		||||
// non-nil *http.Response so that callers can handle redirects, authentication,
 | 
			
		||||
// etcetera. The response body may not contain the entire response and does not
 | 
			
		||||
// need to be closed by the application.
 | 
			
		||||
func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {
 | 
			
		||||
 | 
			
		||||
	if d == nil {
 | 
			
		||||
		d = &Dialer{
 | 
			
		||||
			Proxy: http.ProxyFromEnvironment,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	challengeKey, err := generateChallengeKey()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	u, err := parseURL(urlStr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch u.Scheme {
 | 
			
		||||
	case "ws":
 | 
			
		||||
		u.Scheme = "http"
 | 
			
		||||
	case "wss":
 | 
			
		||||
		u.Scheme = "https"
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, nil, errMalformedURL
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if u.User != nil {
 | 
			
		||||
		// User name and password are not allowed in websocket URIs.
 | 
			
		||||
		return nil, nil, errMalformedURL
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	req := &http.Request{
 | 
			
		||||
		Method:     "GET",
 | 
			
		||||
		URL:        u,
 | 
			
		||||
		Proto:      "HTTP/1.1",
 | 
			
		||||
		ProtoMajor: 1,
 | 
			
		||||
		ProtoMinor: 1,
 | 
			
		||||
		Header:     make(http.Header),
 | 
			
		||||
		Host:       u.Host,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Set the cookies present in the cookie jar of the dialer
 | 
			
		||||
	if d.Jar != nil {
 | 
			
		||||
		for _, cookie := range d.Jar.Cookies(u) {
 | 
			
		||||
			req.AddCookie(cookie)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Set the request headers using the capitalization for names and values in
 | 
			
		||||
	// RFC examples. Although the capitalization shouldn't matter, there are
 | 
			
		||||
	// servers that depend on it. The Header.Set method is not used because the
 | 
			
		||||
	// method canonicalizes the header names.
 | 
			
		||||
	req.Header["Upgrade"] = []string{"websocket"}
 | 
			
		||||
	req.Header["Connection"] = []string{"Upgrade"}
 | 
			
		||||
	req.Header["Sec-WebSocket-Key"] = []string{challengeKey}
 | 
			
		||||
	req.Header["Sec-WebSocket-Version"] = []string{"13"}
 | 
			
		||||
	if len(d.Subprotocols) > 0 {
 | 
			
		||||
		req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")}
 | 
			
		||||
	}
 | 
			
		||||
	for k, vs := range requestHeader {
 | 
			
		||||
		switch {
 | 
			
		||||
		case k == "Host":
 | 
			
		||||
			if len(vs) > 0 {
 | 
			
		||||
				req.Host = vs[0]
 | 
			
		||||
			}
 | 
			
		||||
		case k == "Upgrade" ||
 | 
			
		||||
			k == "Connection" ||
 | 
			
		||||
			k == "Sec-Websocket-Key" ||
 | 
			
		||||
			k == "Sec-Websocket-Version" ||
 | 
			
		||||
			k == "Sec-Websocket-Extensions" ||
 | 
			
		||||
			(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
 | 
			
		||||
			return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
 | 
			
		||||
		default:
 | 
			
		||||
			req.Header[k] = vs
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if d.EnableCompression {
 | 
			
		||||
		req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hostPort, hostNoPort := hostPortNoPort(u)
 | 
			
		||||
 | 
			
		||||
	var proxyURL *url.URL
 | 
			
		||||
	// Check wether the proxy method has been configured
 | 
			
		||||
	if d.Proxy != nil {
 | 
			
		||||
		proxyURL, err = d.Proxy(req)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var targetHostPort string
 | 
			
		||||
	if proxyURL != nil {
 | 
			
		||||
		targetHostPort, _ = hostPortNoPort(proxyURL)
 | 
			
		||||
	} else {
 | 
			
		||||
		targetHostPort = hostPort
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var deadline time.Time
 | 
			
		||||
	if d.HandshakeTimeout != 0 {
 | 
			
		||||
		deadline = time.Now().Add(d.HandshakeTimeout)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	netDial := d.NetDial
 | 
			
		||||
	if netDial == nil {
 | 
			
		||||
		netDialer := &net.Dialer{Deadline: deadline}
 | 
			
		||||
		netDial = netDialer.Dial
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	netConn, err := netDial("tcp", targetHostPort)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if netConn != nil {
 | 
			
		||||
			netConn.Close()
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	if err := netConn.SetDeadline(deadline); err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if proxyURL != nil {
 | 
			
		||||
		connectHeader := make(http.Header)
 | 
			
		||||
		if user := proxyURL.User; user != nil {
 | 
			
		||||
			proxyUser := user.Username()
 | 
			
		||||
			if proxyPassword, passwordSet := user.Password(); passwordSet {
 | 
			
		||||
				credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword))
 | 
			
		||||
				connectHeader.Set("Proxy-Authorization", "Basic "+credential)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		connectReq := &http.Request{
 | 
			
		||||
			Method: "CONNECT",
 | 
			
		||||
			URL:    &url.URL{Opaque: hostPort},
 | 
			
		||||
			Host:   hostPort,
 | 
			
		||||
			Header: connectHeader,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		connectReq.Write(netConn)
 | 
			
		||||
 | 
			
		||||
		// Read response.
 | 
			
		||||
		// Okay to use and discard buffered reader here, because
 | 
			
		||||
		// TLS server will not speak until spoken to.
 | 
			
		||||
		br := bufio.NewReader(netConn)
 | 
			
		||||
		resp, err := http.ReadResponse(br, connectReq)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if resp.StatusCode != 200 {
 | 
			
		||||
			f := strings.SplitN(resp.Status, " ", 2)
 | 
			
		||||
			return nil, nil, errors.New(f[1])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if u.Scheme == "https" {
 | 
			
		||||
		cfg := cloneTLSConfig(d.TLSClientConfig)
 | 
			
		||||
		if cfg.ServerName == "" {
 | 
			
		||||
			cfg.ServerName = hostNoPort
 | 
			
		||||
		}
 | 
			
		||||
		tlsConn := tls.Client(netConn, cfg)
 | 
			
		||||
		netConn = tlsConn
 | 
			
		||||
		if err := tlsConn.Handshake(); err != nil {
 | 
			
		||||
			return nil, nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if !cfg.InsecureSkipVerify {
 | 
			
		||||
			if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
 | 
			
		||||
				return nil, nil, err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize)
 | 
			
		||||
 | 
			
		||||
	if err := req.Write(netConn); err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp, err := http.ReadResponse(conn.br, req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if d.Jar != nil {
 | 
			
		||||
		if rc := resp.Cookies(); len(rc) > 0 {
 | 
			
		||||
			d.Jar.SetCookies(u, rc)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if resp.StatusCode != 101 ||
 | 
			
		||||
		!strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") ||
 | 
			
		||||
		!strings.EqualFold(resp.Header.Get("Connection"), "upgrade") ||
 | 
			
		||||
		resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) {
 | 
			
		||||
		// Before closing the network connection on return from this
 | 
			
		||||
		// function, slurp up some of the response to aid application
 | 
			
		||||
		// debugging.
 | 
			
		||||
		buf := make([]byte, 1024)
 | 
			
		||||
		n, _ := io.ReadFull(resp.Body, buf)
 | 
			
		||||
		resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n]))
 | 
			
		||||
		return nil, resp, ErrBadHandshake
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, ext := range parseExtensions(resp.Header) {
 | 
			
		||||
		if ext[""] != "permessage-deflate" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		_, snct := ext["server_no_context_takeover"]
 | 
			
		||||
		_, cnct := ext["client_no_context_takeover"]
 | 
			
		||||
		if !snct || !cnct {
 | 
			
		||||
			return nil, resp, errInvalidCompression
 | 
			
		||||
		}
 | 
			
		||||
		conn.newCompressionWriter = compressNoContextTakeover
 | 
			
		||||
		conn.newDecompressionReader = decompressNoContextTakeover
 | 
			
		||||
		break
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
 | 
			
		||||
	conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
 | 
			
		||||
 | 
			
		||||
	netConn.SetDeadline(time.Time{})
 | 
			
		||||
	netConn = nil // to avoid close in defer.
 | 
			
		||||
	return conn, resp, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build go1.8
 | 
			
		||||
 | 
			
		||||
package websocket
 | 
			
		||||
 | 
			
		||||
import "crypto/tls"
 | 
			
		||||
 | 
			
		||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
 | 
			
		||||
	if cfg == nil {
 | 
			
		||||
		return &tls.Config{}
 | 
			
		||||
	}
 | 
			
		||||
	return cfg.Clone()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,38 @@
 | 
			
		|||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !go1.8
 | 
			
		||||
 | 
			
		||||
package websocket
 | 
			
		||||
 | 
			
		||||
import "crypto/tls"
 | 
			
		||||
 | 
			
		||||
// cloneTLSConfig clones all public fields except the fields
 | 
			
		||||
// SessionTicketsDisabled and SessionTicketKey. This avoids copying the
 | 
			
		||||
// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a
 | 
			
		||||
// config in active use.
 | 
			
		||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
 | 
			
		||||
	if cfg == nil {
 | 
			
		||||
		return &tls.Config{}
 | 
			
		||||
	}
 | 
			
		||||
	return &tls.Config{
 | 
			
		||||
		Rand:                     cfg.Rand,
 | 
			
		||||
		Time:                     cfg.Time,
 | 
			
		||||
		Certificates:             cfg.Certificates,
 | 
			
		||||
		NameToCertificate:        cfg.NameToCertificate,
 | 
			
		||||
		GetCertificate:           cfg.GetCertificate,
 | 
			
		||||
		RootCAs:                  cfg.RootCAs,
 | 
			
		||||
		NextProtos:               cfg.NextProtos,
 | 
			
		||||
		ServerName:               cfg.ServerName,
 | 
			
		||||
		ClientAuth:               cfg.ClientAuth,
 | 
			
		||||
		ClientCAs:                cfg.ClientCAs,
 | 
			
		||||
		InsecureSkipVerify:       cfg.InsecureSkipVerify,
 | 
			
		||||
		CipherSuites:             cfg.CipherSuites,
 | 
			
		||||
		PreferServerCipherSuites: cfg.PreferServerCipherSuites,
 | 
			
		||||
		ClientSessionCache:       cfg.ClientSessionCache,
 | 
			
		||||
		MinVersion:               cfg.MinVersion,
 | 
			
		||||
		MaxVersion:               cfg.MaxVersion,
 | 
			
		||||
		CurvePreferences:         cfg.CurvePreferences,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,148 @@
 | 
			
		|||
// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package websocket
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"compress/flate"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	minCompressionLevel     = -2 // flate.HuffmanOnly not defined in Go < 1.6
 | 
			
		||||
	maxCompressionLevel     = flate.BestCompression
 | 
			
		||||
	defaultCompressionLevel = 1
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool
 | 
			
		||||
	flateReaderPool  = sync.Pool{New: func() interface{} {
 | 
			
		||||
		return flate.NewReader(nil)
 | 
			
		||||
	}}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func decompressNoContextTakeover(r io.Reader) io.ReadCloser {
 | 
			
		||||
	const tail =
 | 
			
		||||
	// Add four bytes as specified in RFC
 | 
			
		||||
	"\x00\x00\xff\xff" +
 | 
			
		||||
		// Add final block to squelch unexpected EOF error from flate reader.
 | 
			
		||||
		"\x01\x00\x00\xff\xff"
 | 
			
		||||
 | 
			
		||||
	fr, _ := flateReaderPool.Get().(io.ReadCloser)
 | 
			
		||||
	fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil)
 | 
			
		||||
	return &flateReadWrapper{fr}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isValidCompressionLevel(level int) bool {
 | 
			
		||||
	return minCompressionLevel <= level && level <= maxCompressionLevel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser {
 | 
			
		||||
	p := &flateWriterPools[level-minCompressionLevel]
 | 
			
		||||
	tw := &truncWriter{w: w}
 | 
			
		||||
	fw, _ := p.Get().(*flate.Writer)
 | 
			
		||||
	if fw == nil {
 | 
			
		||||
		fw, _ = flate.NewWriter(tw, level)
 | 
			
		||||
	} else {
 | 
			
		||||
		fw.Reset(tw)
 | 
			
		||||
	}
 | 
			
		||||
	return &flateWriteWrapper{fw: fw, tw: tw, p: p}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// truncWriter is an io.Writer that writes all but the last four bytes of the
 | 
			
		||||
// stream to another io.Writer.
 | 
			
		||||
type truncWriter struct {
 | 
			
		||||
	w io.WriteCloser
 | 
			
		||||
	n int
 | 
			
		||||
	p [4]byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *truncWriter) Write(p []byte) (int, error) {
 | 
			
		||||
	n := 0
 | 
			
		||||
 | 
			
		||||
	// fill buffer first for simplicity.
 | 
			
		||||
	if w.n < len(w.p) {
 | 
			
		||||
		n = copy(w.p[w.n:], p)
 | 
			
		||||
		p = p[n:]
 | 
			
		||||
		w.n += n
 | 
			
		||||
		if len(p) == 0 {
 | 
			
		||||
			return n, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m := len(p)
 | 
			
		||||
	if m > len(w.p) {
 | 
			
		||||
		m = len(w.p)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if nn, err := w.w.Write(w.p[:m]); err != nil {
 | 
			
		||||
		return n + nn, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	copy(w.p[:], w.p[m:])
 | 
			
		||||
	copy(w.p[len(w.p)-m:], p[len(p)-m:])
 | 
			
		||||
	nn, err := w.w.Write(p[:len(p)-m])
 | 
			
		||||
	return n + nn, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type flateWriteWrapper struct {
 | 
			
		||||
	fw *flate.Writer
 | 
			
		||||
	tw *truncWriter
 | 
			
		||||
	p  *sync.Pool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *flateWriteWrapper) Write(p []byte) (int, error) {
 | 
			
		||||
	if w.fw == nil {
 | 
			
		||||
		return 0, errWriteClosed
 | 
			
		||||
	}
 | 
			
		||||
	return w.fw.Write(p)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *flateWriteWrapper) Close() error {
 | 
			
		||||
	if w.fw == nil {
 | 
			
		||||
		return errWriteClosed
 | 
			
		||||
	}
 | 
			
		||||
	err1 := w.fw.Flush()
 | 
			
		||||
	w.p.Put(w.fw)
 | 
			
		||||
	w.fw = nil
 | 
			
		||||
	if w.tw.p != [4]byte{0, 0, 0xff, 0xff} {
 | 
			
		||||
		return errors.New("websocket: internal error, unexpected bytes at end of flate stream")
 | 
			
		||||
	}
 | 
			
		||||
	err2 := w.tw.w.Close()
 | 
			
		||||
	if err1 != nil {
 | 
			
		||||
		return err1
 | 
			
		||||
	}
 | 
			
		||||
	return err2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type flateReadWrapper struct {
 | 
			
		||||
	fr io.ReadCloser
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *flateReadWrapper) Read(p []byte) (int, error) {
 | 
			
		||||
	if r.fr == nil {
 | 
			
		||||
		return 0, io.ErrClosedPipe
 | 
			
		||||
	}
 | 
			
		||||
	n, err := r.fr.Read(p)
 | 
			
		||||
	if err == io.EOF {
 | 
			
		||||
		// Preemptively place the reader back in the pool. This helps with
 | 
			
		||||
		// scenarios where the application does not call NextReader() soon after
 | 
			
		||||
		// this final read.
 | 
			
		||||
		r.Close()
 | 
			
		||||
	}
 | 
			
		||||
	return n, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *flateReadWrapper) Close() error {
 | 
			
		||||
	if r.fr == nil {
 | 
			
		||||
		return io.ErrClosedPipe
 | 
			
		||||
	}
 | 
			
		||||
	err := r.fr.Close()
 | 
			
		||||
	flateReaderPool.Put(r.fr)
 | 
			
		||||
	r.fr = nil
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build go1.5
 | 
			
		||||
 | 
			
		||||
package websocket
 | 
			
		||||
 | 
			
		||||
import "io"
 | 
			
		||||
 | 
			
		||||
func (c *Conn) read(n int) ([]byte, error) {
 | 
			
		||||
	p, err := c.br.Peek(n)
 | 
			
		||||
	if err == io.EOF {
 | 
			
		||||
		err = errUnexpectedEOF
 | 
			
		||||
	}
 | 
			
		||||
	c.br.Discard(len(p))
 | 
			
		||||
	return p, err
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !go1.5
 | 
			
		||||
 | 
			
		||||
package websocket
 | 
			
		||||
 | 
			
		||||
import "io"
 | 
			
		||||
 | 
			
		||||
func (c *Conn) read(n int) ([]byte, error) {
 | 
			
		||||
	p, err := c.br.Peek(n)
 | 
			
		||||
	if err == io.EOF {
 | 
			
		||||
		err = errUnexpectedEOF
 | 
			
		||||
	}
 | 
			
		||||
	if len(p) > 0 {
 | 
			
		||||
		// advance over the bytes just read
 | 
			
		||||
		io.ReadFull(c.br, p)
 | 
			
		||||
	}
 | 
			
		||||
	return p, err
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,180 @@
 | 
			
		|||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// Package websocket implements the WebSocket protocol defined in RFC 6455.
 | 
			
		||||
//
 | 
			
		||||
// Overview
 | 
			
		||||
//
 | 
			
		||||
// The Conn type represents a WebSocket connection. A server application uses
 | 
			
		||||
// the Upgrade function from an Upgrader object with a HTTP request handler
 | 
			
		||||
// to get a pointer to a Conn:
 | 
			
		||||
//
 | 
			
		||||
//  var upgrader = websocket.Upgrader{
 | 
			
		||||
//      ReadBufferSize:  1024,
 | 
			
		||||
//      WriteBufferSize: 1024,
 | 
			
		||||
//  }
 | 
			
		||||
//
 | 
			
		||||
//  func handler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
//      conn, err := upgrader.Upgrade(w, r, nil)
 | 
			
		||||
//      if err != nil {
 | 
			
		||||
//          log.Println(err)
 | 
			
		||||
//          return
 | 
			
		||||
//      }
 | 
			
		||||
//      ... Use conn to send and receive messages.
 | 
			
		||||
//  }
 | 
			
		||||
//
 | 
			
		||||
// Call the connection's WriteMessage and ReadMessage methods to send and
 | 
			
		||||
// receive messages as a slice of bytes. This snippet of code shows how to echo
 | 
			
		||||
// messages using these methods:
 | 
			
		||||
//
 | 
			
		||||
//  for {
 | 
			
		||||
//      messageType, p, err := conn.ReadMessage()
 | 
			
		||||
//      if err != nil {
 | 
			
		||||
//          return
 | 
			
		||||
//      }
 | 
			
		||||
//      if err = conn.WriteMessage(messageType, p); err != nil {
 | 
			
		||||
//          return err
 | 
			
		||||
//      }
 | 
			
		||||
//  }
 | 
			
		||||
//
 | 
			
		||||
// In above snippet of code, p is a []byte and messageType is an int with value
 | 
			
		||||
// websocket.BinaryMessage or websocket.TextMessage.
 | 
			
		||||
//
 | 
			
		||||
// An application can also send and receive messages using the io.WriteCloser
 | 
			
		||||
// and io.Reader interfaces. To send a message, call the connection NextWriter
 | 
			
		||||
// method to get an io.WriteCloser, write the message to the writer and close
 | 
			
		||||
// the writer when done. To receive a message, call the connection NextReader
 | 
			
		||||
// method to get an io.Reader and read until io.EOF is returned. This snippet
 | 
			
		||||
// shows how to echo messages using the NextWriter and NextReader methods:
 | 
			
		||||
//
 | 
			
		||||
//  for {
 | 
			
		||||
//      messageType, r, err := conn.NextReader()
 | 
			
		||||
//      if err != nil {
 | 
			
		||||
//          return
 | 
			
		||||
//      }
 | 
			
		||||
//      w, err := conn.NextWriter(messageType)
 | 
			
		||||
//      if err != nil {
 | 
			
		||||
//          return err
 | 
			
		||||
//      }
 | 
			
		||||
//      if _, err := io.Copy(w, r); err != nil {
 | 
			
		||||
//          return err
 | 
			
		||||
//      }
 | 
			
		||||
//      if err := w.Close(); err != nil {
 | 
			
		||||
//          return err
 | 
			
		||||
//      }
 | 
			
		||||
//  }
 | 
			
		||||
//
 | 
			
		||||
// Data Messages
 | 
			
		||||
//
 | 
			
		||||
// The WebSocket protocol distinguishes between text and binary data messages.
 | 
			
		||||
// Text messages are interpreted as UTF-8 encoded text. The interpretation of
 | 
			
		||||
// binary messages is left to the application.
 | 
			
		||||
//
 | 
			
		||||
// This package uses the TextMessage and BinaryMessage integer constants to
 | 
			
		||||
// identify the two data message types. The ReadMessage and NextReader methods
 | 
			
		||||
// return the type of the received message. The messageType argument to the
 | 
			
		||||
// WriteMessage and NextWriter methods specifies the type of a sent message.
 | 
			
		||||
//
 | 
			
		||||
// It is the application's responsibility to ensure that text messages are
 | 
			
		||||
// valid UTF-8 encoded text.
 | 
			
		||||
//
 | 
			
		||||
// Control Messages
 | 
			
		||||
//
 | 
			
		||||
// The WebSocket protocol defines three types of control messages: close, ping
 | 
			
		||||
// and pong. Call the connection WriteControl, WriteMessage or NextWriter
 | 
			
		||||
// methods to send a control message to the peer.
 | 
			
		||||
//
 | 
			
		||||
// Connections handle received close messages by sending a close message to the
 | 
			
		||||
// peer and returning a *CloseError from the the NextReader, ReadMessage or the
 | 
			
		||||
// message Read method.
 | 
			
		||||
//
 | 
			
		||||
// Connections handle received ping and pong messages by invoking callback
 | 
			
		||||
// functions set with SetPingHandler and SetPongHandler methods. The callback
 | 
			
		||||
// functions are called from the NextReader, ReadMessage and the message Read
 | 
			
		||||
// methods.
 | 
			
		||||
//
 | 
			
		||||
// The default ping handler sends a pong to the peer. The application's reading
 | 
			
		||||
// goroutine can block for a short time while the handler writes the pong data
 | 
			
		||||
// to the connection.
 | 
			
		||||
//
 | 
			
		||||
// The application must read the connection to process ping, pong and close
 | 
			
		||||
// messages sent from the peer. If the application is not otherwise interested
 | 
			
		||||
// in messages from the peer, then the application should start a goroutine to
 | 
			
		||||
// read and discard messages from the peer. A simple example is:
 | 
			
		||||
//
 | 
			
		||||
//  func readLoop(c *websocket.Conn) {
 | 
			
		||||
//      for {
 | 
			
		||||
//          if _, _, err := c.NextReader(); err != nil {
 | 
			
		||||
//              c.Close()
 | 
			
		||||
//              break
 | 
			
		||||
//          }
 | 
			
		||||
//      }
 | 
			
		||||
//  }
 | 
			
		||||
//
 | 
			
		||||
// Concurrency
 | 
			
		||||
//
 | 
			
		||||
// Connections support one concurrent reader and one concurrent writer.
 | 
			
		||||
//
 | 
			
		||||
// Applications are responsible for ensuring that no more than one goroutine
 | 
			
		||||
// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage,
 | 
			
		||||
// WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and
 | 
			
		||||
// that no more than one goroutine calls the read methods (NextReader,
 | 
			
		||||
// SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler)
 | 
			
		||||
// concurrently.
 | 
			
		||||
//
 | 
			
		||||
// The Close and WriteControl methods can be called concurrently with all other
 | 
			
		||||
// methods.
 | 
			
		||||
//
 | 
			
		||||
// Origin Considerations
 | 
			
		||||
//
 | 
			
		||||
// Web browsers allow Javascript applications to open a WebSocket connection to
 | 
			
		||||
// any host. It's up to the server to enforce an origin policy using the Origin
 | 
			
		||||
// request header sent by the browser.
 | 
			
		||||
//
 | 
			
		||||
// The Upgrader calls the function specified in the CheckOrigin field to check
 | 
			
		||||
// the origin. If the CheckOrigin function returns false, then the Upgrade
 | 
			
		||||
// method fails the WebSocket handshake with HTTP status 403.
 | 
			
		||||
//
 | 
			
		||||
// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail
 | 
			
		||||
// the handshake if the Origin request header is present and not equal to the
 | 
			
		||||
// Host request header.
 | 
			
		||||
//
 | 
			
		||||
// An application can allow connections from any origin by specifying a
 | 
			
		||||
// function that always returns true:
 | 
			
		||||
//
 | 
			
		||||
//  var upgrader = websocket.Upgrader{
 | 
			
		||||
//      CheckOrigin: func(r *http.Request) bool { return true },
 | 
			
		||||
//  }
 | 
			
		||||
//
 | 
			
		||||
// The deprecated Upgrade function does not enforce an origin policy. It's the
 | 
			
		||||
// application's responsibility to check the Origin header before calling
 | 
			
		||||
// Upgrade.
 | 
			
		||||
//
 | 
			
		||||
// Compression EXPERIMENTAL
 | 
			
		||||
//
 | 
			
		||||
// Per message compression extensions (RFC 7692) are experimentally supported
 | 
			
		||||
// by this package in a limited capacity. Setting the EnableCompression option
 | 
			
		||||
// to true in Dialer or Upgrader will attempt to negotiate per message deflate
 | 
			
		||||
// support.
 | 
			
		||||
//
 | 
			
		||||
//  var upgrader = websocket.Upgrader{
 | 
			
		||||
//      EnableCompression: true,
 | 
			
		||||
//  }
 | 
			
		||||
//
 | 
			
		||||
// If compression was successfully negotiated with the connection's peer, any
 | 
			
		||||
// message received in compressed form will be automatically decompressed.
 | 
			
		||||
// All Read methods will return uncompressed bytes.
 | 
			
		||||
//
 | 
			
		||||
// Per message compression of messages written to a connection can be enabled
 | 
			
		||||
// or disabled by calling the corresponding Conn method:
 | 
			
		||||
//
 | 
			
		||||
//  conn.EnableWriteCompression(false)
 | 
			
		||||
//
 | 
			
		||||
// Currently this package does not support compression with "context takeover".
 | 
			
		||||
// This means that messages must be compressed and decompressed in isolation,
 | 
			
		||||
// without retaining sliding window or dictionary state across messages. For
 | 
			
		||||
// more details refer to RFC 7692.
 | 
			
		||||
//
 | 
			
		||||
// Use of compression is experimental and may result in decreased performance.
 | 
			
		||||
package websocket
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,55 @@
 | 
			
		|||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package websocket
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// WriteJSON is deprecated, use c.WriteJSON instead.
 | 
			
		||||
func WriteJSON(c *Conn, v interface{}) error {
 | 
			
		||||
	return c.WriteJSON(v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteJSON writes the JSON encoding of v to the connection.
 | 
			
		||||
//
 | 
			
		||||
// See the documentation for encoding/json Marshal for details about the
 | 
			
		||||
// conversion of Go values to JSON.
 | 
			
		||||
func (c *Conn) WriteJSON(v interface{}) error {
 | 
			
		||||
	w, err := c.NextWriter(TextMessage)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	err1 := json.NewEncoder(w).Encode(v)
 | 
			
		||||
	err2 := w.Close()
 | 
			
		||||
	if err1 != nil {
 | 
			
		||||
		return err1
 | 
			
		||||
	}
 | 
			
		||||
	return err2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadJSON is deprecated, use c.ReadJSON instead.
 | 
			
		||||
func ReadJSON(c *Conn, v interface{}) error {
 | 
			
		||||
	return c.ReadJSON(v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadJSON reads the next JSON-encoded message from the connection and stores
 | 
			
		||||
// it in the value pointed to by v.
 | 
			
		||||
//
 | 
			
		||||
// See the documentation for the encoding/json Unmarshal function for details
 | 
			
		||||
// about the conversion of JSON to a Go value.
 | 
			
		||||
func (c *Conn) ReadJSON(v interface{}) error {
 | 
			
		||||
	_, r, err := c.NextReader()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	err = json.NewDecoder(r).Decode(v)
 | 
			
		||||
	if err == io.EOF {
 | 
			
		||||
		// One value is expected in the message.
 | 
			
		||||
		err = io.ErrUnexpectedEOF
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,55 @@
 | 
			
		|||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.  Use of
 | 
			
		||||
// this source code is governed by a BSD-style license that can be found in the
 | 
			
		||||
// LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !appengine
 | 
			
		||||
 | 
			
		||||
package websocket
 | 
			
		||||
 | 
			
		||||
import "unsafe"
 | 
			
		||||
 | 
			
		||||
const wordSize = int(unsafe.Sizeof(uintptr(0)))
 | 
			
		||||
 | 
			
		||||
func maskBytes(key [4]byte, pos int, b []byte) int {
 | 
			
		||||
 | 
			
		||||
	// Mask one byte at a time for small buffers.
 | 
			
		||||
	if len(b) < 2*wordSize {
 | 
			
		||||
		for i := range b {
 | 
			
		||||
			b[i] ^= key[pos&3]
 | 
			
		||||
			pos++
 | 
			
		||||
		}
 | 
			
		||||
		return pos & 3
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Mask one byte at a time to word boundary.
 | 
			
		||||
	if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 {
 | 
			
		||||
		n = wordSize - n
 | 
			
		||||
		for i := range b[:n] {
 | 
			
		||||
			b[i] ^= key[pos&3]
 | 
			
		||||
			pos++
 | 
			
		||||
		}
 | 
			
		||||
		b = b[n:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create aligned word size key.
 | 
			
		||||
	var k [wordSize]byte
 | 
			
		||||
	for i := range k {
 | 
			
		||||
		k[i] = key[(pos+i)&3]
 | 
			
		||||
	}
 | 
			
		||||
	kw := *(*uintptr)(unsafe.Pointer(&k))
 | 
			
		||||
 | 
			
		||||
	// Mask one word at a time.
 | 
			
		||||
	n := (len(b) / wordSize) * wordSize
 | 
			
		||||
	for i := 0; i < n; i += wordSize {
 | 
			
		||||
		*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Mask one byte at a time for remaining bytes.
 | 
			
		||||
	b = b[n:]
 | 
			
		||||
	for i := range b {
 | 
			
		||||
		b[i] ^= key[pos&3]
 | 
			
		||||
		pos++
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return pos & 3
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.  Use of
 | 
			
		||||
// this source code is governed by a BSD-style license that can be found in the
 | 
			
		||||
// LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build appengine
 | 
			
		||||
 | 
			
		||||
package websocket
 | 
			
		||||
 | 
			
		||||
func maskBytes(key [4]byte, pos int, b []byte) int {
 | 
			
		||||
	for i := range b {
 | 
			
		||||
		b[i] ^= key[pos&3]
 | 
			
		||||
		pos++
 | 
			
		||||
	}
 | 
			
		||||
	return pos & 3
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,103 @@
 | 
			
		|||
// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package websocket
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"net"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// PreparedMessage caches on the wire representations of a message payload.
 | 
			
		||||
// Use PreparedMessage to efficiently send a message payload to multiple
 | 
			
		||||
// connections. PreparedMessage is especially useful when compression is used
 | 
			
		||||
// because the CPU and memory expensive compression operation can be executed
 | 
			
		||||
// once for a given set of compression options.
 | 
			
		||||
type PreparedMessage struct {
 | 
			
		||||
	messageType int
 | 
			
		||||
	data        []byte
 | 
			
		||||
	err         error
 | 
			
		||||
	mu          sync.Mutex
 | 
			
		||||
	frames      map[prepareKey]*preparedFrame
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage.
 | 
			
		||||
type prepareKey struct {
 | 
			
		||||
	isServer         bool
 | 
			
		||||
	compress         bool
 | 
			
		||||
	compressionLevel int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// preparedFrame contains data in wire representation.
 | 
			
		||||
type preparedFrame struct {
 | 
			
		||||
	once sync.Once
 | 
			
		||||
	data []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewPreparedMessage returns an initialized PreparedMessage. You can then send
 | 
			
		||||
// it to connection using WritePreparedMessage method. Valid wire
 | 
			
		||||
// representation will be calculated lazily only once for a set of current
 | 
			
		||||
// connection options.
 | 
			
		||||
func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) {
 | 
			
		||||
	pm := &PreparedMessage{
 | 
			
		||||
		messageType: messageType,
 | 
			
		||||
		frames:      make(map[prepareKey]*preparedFrame),
 | 
			
		||||
		data:        data,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Prepare a plain server frame.
 | 
			
		||||
	_, frameData, err := pm.frame(prepareKey{isServer: true, compress: false})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// To protect against caller modifying the data argument, remember the data
 | 
			
		||||
	// copied to the plain server frame.
 | 
			
		||||
	pm.data = frameData[len(frameData)-len(data):]
 | 
			
		||||
	return pm, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) {
 | 
			
		||||
	pm.mu.Lock()
 | 
			
		||||
	frame, ok := pm.frames[key]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		frame = &preparedFrame{}
 | 
			
		||||
		pm.frames[key] = frame
 | 
			
		||||
	}
 | 
			
		||||
	pm.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	frame.once.Do(func() {
 | 
			
		||||
		// Prepare a frame using a 'fake' connection.
 | 
			
		||||
		// TODO: Refactor code in conn.go to allow more direct construction of
 | 
			
		||||
		// the frame.
 | 
			
		||||
		mu := make(chan bool, 1)
 | 
			
		||||
		mu <- true
 | 
			
		||||
		var nc prepareConn
 | 
			
		||||
		c := &Conn{
 | 
			
		||||
			conn:                   &nc,
 | 
			
		||||
			mu:                     mu,
 | 
			
		||||
			isServer:               key.isServer,
 | 
			
		||||
			compressionLevel:       key.compressionLevel,
 | 
			
		||||
			enableWriteCompression: true,
 | 
			
		||||
			writeBuf:               make([]byte, defaultWriteBufferSize+maxFrameHeaderSize),
 | 
			
		||||
		}
 | 
			
		||||
		if key.compress {
 | 
			
		||||
			c.newCompressionWriter = compressNoContextTakeover
 | 
			
		||||
		}
 | 
			
		||||
		err = c.WriteMessage(pm.messageType, pm.data)
 | 
			
		||||
		frame.data = nc.buf.Bytes()
 | 
			
		||||
	})
 | 
			
		||||
	return pm.messageType, frame.data, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type prepareConn struct {
 | 
			
		||||
	buf bytes.Buffer
 | 
			
		||||
	net.Conn
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pc *prepareConn) Write(p []byte) (int, error)        { return pc.buf.Write(p) }
 | 
			
		||||
func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil }
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,291 @@
 | 
			
		|||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package websocket
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// HandshakeError describes an error with the handshake from the peer.
 | 
			
		||||
type HandshakeError struct {
 | 
			
		||||
	message string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e HandshakeError) Error() string { return e.message }
 | 
			
		||||
 | 
			
		||||
// Upgrader specifies parameters for upgrading an HTTP connection to a
 | 
			
		||||
// WebSocket connection.
 | 
			
		||||
type Upgrader struct {
 | 
			
		||||
	// HandshakeTimeout specifies the duration for the handshake to complete.
 | 
			
		||||
	HandshakeTimeout time.Duration
 | 
			
		||||
 | 
			
		||||
	// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer
 | 
			
		||||
	// size is zero, then buffers allocated by the HTTP server are used. The
 | 
			
		||||
	// I/O buffer sizes do not limit the size of the messages that can be sent
 | 
			
		||||
	// or received.
 | 
			
		||||
	ReadBufferSize, WriteBufferSize int
 | 
			
		||||
 | 
			
		||||
	// Subprotocols specifies the server's supported protocols in order of
 | 
			
		||||
	// preference. If this field is set, then the Upgrade method negotiates a
 | 
			
		||||
	// subprotocol by selecting the first match in this list with a protocol
 | 
			
		||||
	// requested by the client.
 | 
			
		||||
	Subprotocols []string
 | 
			
		||||
 | 
			
		||||
	// Error specifies the function for generating HTTP error responses. If Error
 | 
			
		||||
	// is nil, then http.Error is used to generate the HTTP response.
 | 
			
		||||
	Error func(w http.ResponseWriter, r *http.Request, status int, reason error)
 | 
			
		||||
 | 
			
		||||
	// CheckOrigin returns true if the request Origin header is acceptable. If
 | 
			
		||||
	// CheckOrigin is nil, the host in the Origin header must not be set or
 | 
			
		||||
	// must match the host of the request.
 | 
			
		||||
	CheckOrigin func(r *http.Request) bool
 | 
			
		||||
 | 
			
		||||
	// EnableCompression specify if the server should attempt to negotiate per
 | 
			
		||||
	// message compression (RFC 7692). Setting this value to true does not
 | 
			
		||||
	// guarantee that compression will be supported. Currently only "no context
 | 
			
		||||
	// takeover" modes are supported.
 | 
			
		||||
	EnableCompression bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) {
 | 
			
		||||
	err := HandshakeError{reason}
 | 
			
		||||
	if u.Error != nil {
 | 
			
		||||
		u.Error(w, r, status, err)
 | 
			
		||||
	} else {
 | 
			
		||||
		w.Header().Set("Sec-Websocket-Version", "13")
 | 
			
		||||
		http.Error(w, http.StatusText(status), status)
 | 
			
		||||
	}
 | 
			
		||||
	return nil, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// checkSameOrigin returns true if the origin is not set or is equal to the request host.
 | 
			
		||||
func checkSameOrigin(r *http.Request) bool {
 | 
			
		||||
	origin := r.Header["Origin"]
 | 
			
		||||
	if len(origin) == 0 {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	u, err := url.Parse(origin[0])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return u.Host == r.Host
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string {
 | 
			
		||||
	if u.Subprotocols != nil {
 | 
			
		||||
		clientProtocols := Subprotocols(r)
 | 
			
		||||
		for _, serverProtocol := range u.Subprotocols {
 | 
			
		||||
			for _, clientProtocol := range clientProtocols {
 | 
			
		||||
				if clientProtocol == serverProtocol {
 | 
			
		||||
					return clientProtocol
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else if responseHeader != nil {
 | 
			
		||||
		return responseHeader.Get("Sec-Websocket-Protocol")
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
 | 
			
		||||
//
 | 
			
		||||
// The responseHeader is included in the response to the client's upgrade
 | 
			
		||||
// request. Use the responseHeader to specify cookies (Set-Cookie) and the
 | 
			
		||||
// application negotiated subprotocol (Sec-Websocket-Protocol).
 | 
			
		||||
//
 | 
			
		||||
// If the upgrade fails, then Upgrade replies to the client with an HTTP error
 | 
			
		||||
// response.
 | 
			
		||||
func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
 | 
			
		||||
	if r.Method != "GET" {
 | 
			
		||||
		return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: not a websocket handshake: request method is not GET")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok {
 | 
			
		||||
		return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-Websocket-Extensions' headers are unsupported")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !tokenListContainsValue(r.Header, "Connection", "upgrade") {
 | 
			
		||||
		return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'upgrade' token not found in 'Connection' header")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
 | 
			
		||||
		return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'websocket' token not found in 'Upgrade' header")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") {
 | 
			
		||||
		return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	checkOrigin := u.CheckOrigin
 | 
			
		||||
	if checkOrigin == nil {
 | 
			
		||||
		checkOrigin = checkSameOrigin
 | 
			
		||||
	}
 | 
			
		||||
	if !checkOrigin(r) {
 | 
			
		||||
		return u.returnError(w, r, http.StatusForbidden, "websocket: 'Origin' header value not allowed")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	challengeKey := r.Header.Get("Sec-Websocket-Key")
 | 
			
		||||
	if challengeKey == "" {
 | 
			
		||||
		return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: `Sec-Websocket-Key' header is missing or blank")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	subprotocol := u.selectSubprotocol(r, responseHeader)
 | 
			
		||||
 | 
			
		||||
	// Negotiate PMCE
 | 
			
		||||
	var compress bool
 | 
			
		||||
	if u.EnableCompression {
 | 
			
		||||
		for _, ext := range parseExtensions(r.Header) {
 | 
			
		||||
			if ext[""] != "permessage-deflate" {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			compress = true
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		netConn net.Conn
 | 
			
		||||
		err     error
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	h, ok := w.(http.Hijacker)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker")
 | 
			
		||||
	}
 | 
			
		||||
	var brw *bufio.ReadWriter
 | 
			
		||||
	netConn, brw, err = h.Hijack()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return u.returnError(w, r, http.StatusInternalServerError, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if brw.Reader.Buffered() > 0 {
 | 
			
		||||
		netConn.Close()
 | 
			
		||||
		return nil, errors.New("websocket: client sent data before handshake is complete")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c := newConnBRW(netConn, true, u.ReadBufferSize, u.WriteBufferSize, brw)
 | 
			
		||||
	c.subprotocol = subprotocol
 | 
			
		||||
 | 
			
		||||
	if compress {
 | 
			
		||||
		c.newCompressionWriter = compressNoContextTakeover
 | 
			
		||||
		c.newDecompressionReader = decompressNoContextTakeover
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p := c.writeBuf[:0]
 | 
			
		||||
	p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...)
 | 
			
		||||
	p = append(p, computeAcceptKey(challengeKey)...)
 | 
			
		||||
	p = append(p, "\r\n"...)
 | 
			
		||||
	if c.subprotocol != "" {
 | 
			
		||||
		p = append(p, "Sec-Websocket-Protocol: "...)
 | 
			
		||||
		p = append(p, c.subprotocol...)
 | 
			
		||||
		p = append(p, "\r\n"...)
 | 
			
		||||
	}
 | 
			
		||||
	if compress {
 | 
			
		||||
		p = append(p, "Sec-Websocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...)
 | 
			
		||||
	}
 | 
			
		||||
	for k, vs := range responseHeader {
 | 
			
		||||
		if k == "Sec-Websocket-Protocol" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		for _, v := range vs {
 | 
			
		||||
			p = append(p, k...)
 | 
			
		||||
			p = append(p, ": "...)
 | 
			
		||||
			for i := 0; i < len(v); i++ {
 | 
			
		||||
				b := v[i]
 | 
			
		||||
				if b <= 31 {
 | 
			
		||||
					// prevent response splitting.
 | 
			
		||||
					b = ' '
 | 
			
		||||
				}
 | 
			
		||||
				p = append(p, b)
 | 
			
		||||
			}
 | 
			
		||||
			p = append(p, "\r\n"...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	p = append(p, "\r\n"...)
 | 
			
		||||
 | 
			
		||||
	// Clear deadlines set by HTTP server.
 | 
			
		||||
	netConn.SetDeadline(time.Time{})
 | 
			
		||||
 | 
			
		||||
	if u.HandshakeTimeout > 0 {
 | 
			
		||||
		netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout))
 | 
			
		||||
	}
 | 
			
		||||
	if _, err = netConn.Write(p); err != nil {
 | 
			
		||||
		netConn.Close()
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if u.HandshakeTimeout > 0 {
 | 
			
		||||
		netConn.SetWriteDeadline(time.Time{})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
 | 
			
		||||
//
 | 
			
		||||
// This function is deprecated, use websocket.Upgrader instead.
 | 
			
		||||
//
 | 
			
		||||
// The application is responsible for checking the request origin before
 | 
			
		||||
// calling Upgrade. An example implementation of the same origin policy is:
 | 
			
		||||
//
 | 
			
		||||
//	if req.Header.Get("Origin") != "http://"+req.Host {
 | 
			
		||||
//		http.Error(w, "Origin not allowed", 403)
 | 
			
		||||
//		return
 | 
			
		||||
//	}
 | 
			
		||||
//
 | 
			
		||||
// If the endpoint supports subprotocols, then the application is responsible
 | 
			
		||||
// for negotiating the protocol used on the connection. Use the Subprotocols()
 | 
			
		||||
// function to get the subprotocols requested by the client. Use the
 | 
			
		||||
// Sec-Websocket-Protocol response header to specify the subprotocol selected
 | 
			
		||||
// by the application.
 | 
			
		||||
//
 | 
			
		||||
// The responseHeader is included in the response to the client's upgrade
 | 
			
		||||
// request. Use the responseHeader to specify cookies (Set-Cookie) and the
 | 
			
		||||
// negotiated subprotocol (Sec-Websocket-Protocol).
 | 
			
		||||
//
 | 
			
		||||
// The connection buffers IO to the underlying network connection. The
 | 
			
		||||
// readBufSize and writeBufSize parameters specify the size of the buffers to
 | 
			
		||||
// use. Messages can be larger than the buffers.
 | 
			
		||||
//
 | 
			
		||||
// If the request is not a valid WebSocket handshake, then Upgrade returns an
 | 
			
		||||
// error of type HandshakeError. Applications should handle this error by
 | 
			
		||||
// replying to the client with an HTTP error response.
 | 
			
		||||
func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) {
 | 
			
		||||
	u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize}
 | 
			
		||||
	u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) {
 | 
			
		||||
		// don't return errors to maintain backwards compatibility
 | 
			
		||||
	}
 | 
			
		||||
	u.CheckOrigin = func(r *http.Request) bool {
 | 
			
		||||
		// allow all connections by default
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return u.Upgrade(w, r, responseHeader)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Subprotocols returns the subprotocols requested by the client in the
 | 
			
		||||
// Sec-Websocket-Protocol header.
 | 
			
		||||
func Subprotocols(r *http.Request) []string {
 | 
			
		||||
	h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol"))
 | 
			
		||||
	if h == "" {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	protocols := strings.Split(h, ",")
 | 
			
		||||
	for i := range protocols {
 | 
			
		||||
		protocols[i] = strings.TrimSpace(protocols[i])
 | 
			
		||||
	}
 | 
			
		||||
	return protocols
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsWebSocketUpgrade returns true if the client requested upgrade to the
 | 
			
		||||
// WebSocket protocol.
 | 
			
		||||
func IsWebSocketUpgrade(r *http.Request) bool {
 | 
			
		||||
	return tokenListContainsValue(r.Header, "Connection", "upgrade") &&
 | 
			
		||||
		tokenListContainsValue(r.Header, "Upgrade", "websocket")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,214 @@
 | 
			
		|||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package websocket
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/sha1"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
 | 
			
		||||
 | 
			
		||||
func computeAcceptKey(challengeKey string) string {
 | 
			
		||||
	h := sha1.New()
 | 
			
		||||
	h.Write([]byte(challengeKey))
 | 
			
		||||
	h.Write(keyGUID)
 | 
			
		||||
	return base64.StdEncoding.EncodeToString(h.Sum(nil))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func generateChallengeKey() (string, error) {
 | 
			
		||||
	p := make([]byte, 16)
 | 
			
		||||
	if _, err := io.ReadFull(rand.Reader, p); err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return base64.StdEncoding.EncodeToString(p), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Octet types from RFC 2616.
 | 
			
		||||
var octetTypes [256]byte
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	isTokenOctet = 1 << iota
 | 
			
		||||
	isSpaceOctet
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	// From RFC 2616
 | 
			
		||||
	//
 | 
			
		||||
	// OCTET      = <any 8-bit sequence of data>
 | 
			
		||||
	// CHAR       = <any US-ASCII character (octets 0 - 127)>
 | 
			
		||||
	// CTL        = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
 | 
			
		||||
	// CR         = <US-ASCII CR, carriage return (13)>
 | 
			
		||||
	// LF         = <US-ASCII LF, linefeed (10)>
 | 
			
		||||
	// SP         = <US-ASCII SP, space (32)>
 | 
			
		||||
	// HT         = <US-ASCII HT, horizontal-tab (9)>
 | 
			
		||||
	// <">        = <US-ASCII double-quote mark (34)>
 | 
			
		||||
	// CRLF       = CR LF
 | 
			
		||||
	// LWS        = [CRLF] 1*( SP | HT )
 | 
			
		||||
	// TEXT       = <any OCTET except CTLs, but including LWS>
 | 
			
		||||
	// separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <">
 | 
			
		||||
	//              | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
 | 
			
		||||
	// token      = 1*<any CHAR except CTLs or separators>
 | 
			
		||||
	// qdtext     = <any TEXT except <">>
 | 
			
		||||
 | 
			
		||||
	for c := 0; c < 256; c++ {
 | 
			
		||||
		var t byte
 | 
			
		||||
		isCtl := c <= 31 || c == 127
 | 
			
		||||
		isChar := 0 <= c && c <= 127
 | 
			
		||||
		isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0
 | 
			
		||||
		if strings.IndexRune(" \t\r\n", rune(c)) >= 0 {
 | 
			
		||||
			t |= isSpaceOctet
 | 
			
		||||
		}
 | 
			
		||||
		if isChar && !isCtl && !isSeparator {
 | 
			
		||||
			t |= isTokenOctet
 | 
			
		||||
		}
 | 
			
		||||
		octetTypes[c] = t
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func skipSpace(s string) (rest string) {
 | 
			
		||||
	i := 0
 | 
			
		||||
	for ; i < len(s); i++ {
 | 
			
		||||
		if octetTypes[s[i]]&isSpaceOctet == 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return s[i:]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func nextToken(s string) (token, rest string) {
 | 
			
		||||
	i := 0
 | 
			
		||||
	for ; i < len(s); i++ {
 | 
			
		||||
		if octetTypes[s[i]]&isTokenOctet == 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return s[:i], s[i:]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func nextTokenOrQuoted(s string) (value string, rest string) {
 | 
			
		||||
	if !strings.HasPrefix(s, "\"") {
 | 
			
		||||
		return nextToken(s)
 | 
			
		||||
	}
 | 
			
		||||
	s = s[1:]
 | 
			
		||||
	for i := 0; i < len(s); i++ {
 | 
			
		||||
		switch s[i] {
 | 
			
		||||
		case '"':
 | 
			
		||||
			return s[:i], s[i+1:]
 | 
			
		||||
		case '\\':
 | 
			
		||||
			p := make([]byte, len(s)-1)
 | 
			
		||||
			j := copy(p, s[:i])
 | 
			
		||||
			escape := true
 | 
			
		||||
			for i = i + 1; i < len(s); i++ {
 | 
			
		||||
				b := s[i]
 | 
			
		||||
				switch {
 | 
			
		||||
				case escape:
 | 
			
		||||
					escape = false
 | 
			
		||||
					p[j] = b
 | 
			
		||||
					j += 1
 | 
			
		||||
				case b == '\\':
 | 
			
		||||
					escape = true
 | 
			
		||||
				case b == '"':
 | 
			
		||||
					return string(p[:j]), s[i+1:]
 | 
			
		||||
				default:
 | 
			
		||||
					p[j] = b
 | 
			
		||||
					j += 1
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return "", ""
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return "", ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// tokenListContainsValue returns true if the 1#token header with the given
 | 
			
		||||
// name contains token.
 | 
			
		||||
func tokenListContainsValue(header http.Header, name string, value string) bool {
 | 
			
		||||
headers:
 | 
			
		||||
	for _, s := range header[name] {
 | 
			
		||||
		for {
 | 
			
		||||
			var t string
 | 
			
		||||
			t, s = nextToken(skipSpace(s))
 | 
			
		||||
			if t == "" {
 | 
			
		||||
				continue headers
 | 
			
		||||
			}
 | 
			
		||||
			s = skipSpace(s)
 | 
			
		||||
			if s != "" && s[0] != ',' {
 | 
			
		||||
				continue headers
 | 
			
		||||
			}
 | 
			
		||||
			if strings.EqualFold(t, value) {
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
			if s == "" {
 | 
			
		||||
				continue headers
 | 
			
		||||
			}
 | 
			
		||||
			s = s[1:]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parseExtensiosn parses WebSocket extensions from a header.
 | 
			
		||||
func parseExtensions(header http.Header) []map[string]string {
 | 
			
		||||
 | 
			
		||||
	// From RFC 6455:
 | 
			
		||||
	//
 | 
			
		||||
	//  Sec-WebSocket-Extensions = extension-list
 | 
			
		||||
	//  extension-list = 1#extension
 | 
			
		||||
	//  extension = extension-token *( ";" extension-param )
 | 
			
		||||
	//  extension-token = registered-token
 | 
			
		||||
	//  registered-token = token
 | 
			
		||||
	//  extension-param = token [ "=" (token | quoted-string) ]
 | 
			
		||||
	//     ;When using the quoted-string syntax variant, the value
 | 
			
		||||
	//     ;after quoted-string unescaping MUST conform to the
 | 
			
		||||
	//     ;'token' ABNF.
 | 
			
		||||
 | 
			
		||||
	var result []map[string]string
 | 
			
		||||
headers:
 | 
			
		||||
	for _, s := range header["Sec-Websocket-Extensions"] {
 | 
			
		||||
		for {
 | 
			
		||||
			var t string
 | 
			
		||||
			t, s = nextToken(skipSpace(s))
 | 
			
		||||
			if t == "" {
 | 
			
		||||
				continue headers
 | 
			
		||||
			}
 | 
			
		||||
			ext := map[string]string{"": t}
 | 
			
		||||
			for {
 | 
			
		||||
				s = skipSpace(s)
 | 
			
		||||
				if !strings.HasPrefix(s, ";") {
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
				var k string
 | 
			
		||||
				k, s = nextToken(skipSpace(s[1:]))
 | 
			
		||||
				if k == "" {
 | 
			
		||||
					continue headers
 | 
			
		||||
				}
 | 
			
		||||
				s = skipSpace(s)
 | 
			
		||||
				var v string
 | 
			
		||||
				if strings.HasPrefix(s, "=") {
 | 
			
		||||
					v, s = nextTokenOrQuoted(skipSpace(s[1:]))
 | 
			
		||||
					s = skipSpace(s)
 | 
			
		||||
				}
 | 
			
		||||
				if s != "" && s[0] != ',' && s[0] != ';' {
 | 
			
		||||
					continue headers
 | 
			
		||||
				}
 | 
			
		||||
				ext[k] = v
 | 
			
		||||
			}
 | 
			
		||||
			if s != "" && s[0] != ',' {
 | 
			
		||||
				continue headers
 | 
			
		||||
			}
 | 
			
		||||
			result = append(result, ext)
 | 
			
		||||
			if s == "" {
 | 
			
		||||
				continue headers
 | 
			
		||||
			}
 | 
			
		||||
			s = s[1:]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
Copyright (c) 2009 The Go Authors. All rights reserved.
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are
 | 
			
		||||
met:
 | 
			
		||||
 | 
			
		||||
   * Redistributions of source code must retain the above copyright
 | 
			
		||||
notice, this list of conditions and the following disclaimer.
 | 
			
		||||
   * Redistributions in binary form must reproduce the above
 | 
			
		||||
copyright notice, this list of conditions and the following disclaimer
 | 
			
		||||
in the documentation and/or other materials provided with the
 | 
			
		||||
distribution.
 | 
			
		||||
   * Neither the name of Google Inc. nor the names of its
 | 
			
		||||
contributors may be used to endorse or promote products derived from
 | 
			
		||||
this software without specific prior written permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
			
		||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
			
		||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
			
		||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
Additional IP Rights Grant (Patents)
 | 
			
		||||
 | 
			
		||||
"This implementation" means the copyrightable works distributed by
 | 
			
		||||
Google as part of the Go project.
 | 
			
		||||
 | 
			
		||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
 | 
			
		||||
no-charge, royalty-free, irrevocable (except as stated in this section)
 | 
			
		||||
patent license to make, have made, use, offer to sell, sell, import,
 | 
			
		||||
transfer and otherwise run, modify and propagate the contents of this
 | 
			
		||||
implementation of Go, where such license applies only to those patent
 | 
			
		||||
claims, both currently owned or controlled by Google and acquired in
 | 
			
		||||
the future, licensable by Google that are necessarily infringed by this
 | 
			
		||||
implementation of Go.  This grant does not include claims that would be
 | 
			
		||||
infringed only as a consequence of further modification of this
 | 
			
		||||
implementation.  If you or your agent or exclusive licensee institute or
 | 
			
		||||
order or agree to the institution of patent litigation against any
 | 
			
		||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
 | 
			
		||||
that this implementation of Go or any code incorporated within this
 | 
			
		||||
implementation of Go constitutes direct or contributory patent
 | 
			
		||||
infringement, or inducement of patent infringement, then any patent
 | 
			
		||||
rights granted to you under this License for this implementation of Go
 | 
			
		||||
shall terminate as of the date such litigation is filed.
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,173 @@
 | 
			
		|||
# Building `sys/unix`
 | 
			
		||||
 | 
			
		||||
The sys/unix package provides access to the raw system call interface of the
 | 
			
		||||
underlying operating system. See: https://godoc.org/golang.org/x/sys/unix
 | 
			
		||||
 | 
			
		||||
Porting Go to a new architecture/OS combination or adding syscalls, types, or
 | 
			
		||||
constants to an existing architecture/OS pair requires some manual effort;
 | 
			
		||||
however, there are tools that automate much of the process.
 | 
			
		||||
 | 
			
		||||
## Build Systems
 | 
			
		||||
 | 
			
		||||
There are currently two ways we generate the necessary files. We are currently
 | 
			
		||||
migrating the build system to use containers so the builds are reproducible.
 | 
			
		||||
This is being done on an OS-by-OS basis. Please update this documentation as
 | 
			
		||||
components of the build system change.
 | 
			
		||||
 | 
			
		||||
### Old Build System (currently for `GOOS != "Linux" || GOARCH == "sparc64"`)
 | 
			
		||||
 | 
			
		||||
The old build system generates the Go files based on the C header files
 | 
			
		||||
present on your system. This means that files
 | 
			
		||||
for a given GOOS/GOARCH pair must be generated on a system with that OS and
 | 
			
		||||
architecture. This also means that the generated code can differ from system
 | 
			
		||||
to system, based on differences in the header files.
 | 
			
		||||
 | 
			
		||||
To avoid this, if you are using the old build system, only generate the Go
 | 
			
		||||
files on an installation with unmodified header files. It is also important to
 | 
			
		||||
keep track of which version of the OS the files were generated from (ex.
 | 
			
		||||
Darwin 14 vs Darwin 15). This makes it easier to track the progress of changes
 | 
			
		||||
and have each OS upgrade correspond to a single change.
 | 
			
		||||
 | 
			
		||||
To build the files for your current OS and architecture, make sure GOOS and
 | 
			
		||||
GOARCH are set correctly and run `mkall.sh`. This will generate the files for
 | 
			
		||||
your specific system. Running `mkall.sh -n` shows the commands that will be run.
 | 
			
		||||
 | 
			
		||||
Requirements: bash, perl, go
 | 
			
		||||
 | 
			
		||||
### New Build System (currently for `GOOS == "Linux" && GOARCH != "sparc64"`)
 | 
			
		||||
 | 
			
		||||
The new build system uses a Docker container to generate the go files directly
 | 
			
		||||
from source checkouts of the kernel and various system libraries. This means
 | 
			
		||||
that on any platform that supports Docker, all the files using the new build
 | 
			
		||||
system can be generated at once, and generated files will not change based on
 | 
			
		||||
what the person running the scripts has installed on their computer.
 | 
			
		||||
 | 
			
		||||
The OS specific files for the new build system are located in the `${GOOS}`
 | 
			
		||||
directory, and the build is coordinated by the `${GOOS}/mkall.go` program. When
 | 
			
		||||
the kernel or system library updates, modify the Dockerfile at
 | 
			
		||||
`${GOOS}/Dockerfile` to checkout the new release of the source.
 | 
			
		||||
 | 
			
		||||
To build all the files under the new build system, you must be on an amd64/Linux
 | 
			
		||||
system and have your GOOS and GOARCH set accordingly. Running `mkall.sh` will
 | 
			
		||||
then generate all of the files for all of the GOOS/GOARCH pairs in the new build
 | 
			
		||||
system. Running `mkall.sh -n` shows the commands that will be run.
 | 
			
		||||
 | 
			
		||||
Requirements: bash, perl, go, docker
 | 
			
		||||
 | 
			
		||||
## Component files
 | 
			
		||||
 | 
			
		||||
This section describes the various files used in the code generation process.
 | 
			
		||||
It also contains instructions on how to modify these files to add a new
 | 
			
		||||
architecture/OS or to add additional syscalls, types, or constants. Note that
 | 
			
		||||
if you are using the new build system, the scripts cannot be called normally.
 | 
			
		||||
They must be called from within the docker container.
 | 
			
		||||
 | 
			
		||||
### asm files
 | 
			
		||||
 | 
			
		||||
The hand-written assembly file at `asm_${GOOS}_${GOARCH}.s` implements system
 | 
			
		||||
call dispatch. There are three entry points:
 | 
			
		||||
```
 | 
			
		||||
  func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
 | 
			
		||||
  func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
 | 
			
		||||
  func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
 | 
			
		||||
```
 | 
			
		||||
The first and second are the standard ones; they differ only in how many
 | 
			
		||||
arguments can be passed to the kernel. The third is for low-level use by the
 | 
			
		||||
ForkExec wrapper. Unlike the first two, it does not call into the scheduler to
 | 
			
		||||
let it know that a system call is running.
 | 
			
		||||
 | 
			
		||||
When porting Go to an new architecture/OS, this file must be implemented for
 | 
			
		||||
each GOOS/GOARCH pair.
 | 
			
		||||
 | 
			
		||||
### mksysnum
 | 
			
		||||
 | 
			
		||||
Mksysnum is a script located at `${GOOS}/mksysnum.pl` (or `mksysnum_${GOOS}.pl`
 | 
			
		||||
for the old system). This script takes in a list of header files containing the
 | 
			
		||||
syscall number declarations and parses them to produce the corresponding list of
 | 
			
		||||
Go numeric constants. See `zsysnum_${GOOS}_${GOARCH}.go` for the generated
 | 
			
		||||
constants.
 | 
			
		||||
 | 
			
		||||
Adding new syscall numbers is mostly done by running the build on a sufficiently
 | 
			
		||||
new installation of the target OS (or updating the source checkouts for the
 | 
			
		||||
new build system). However, depending on the OS, you make need to update the
 | 
			
		||||
parsing in mksysnum.
 | 
			
		||||
 | 
			
		||||
### mksyscall.pl
 | 
			
		||||
 | 
			
		||||
The `syscall.go`, `syscall_${GOOS}.go`, `syscall_${GOOS}_${GOARCH}.go` are
 | 
			
		||||
hand-written Go files which implement system calls (for unix, the specific OS,
 | 
			
		||||
or the specific OS/Architecture pair respectively) that need special handling
 | 
			
		||||
and list `//sys` comments giving prototypes for ones that can be generated.
 | 
			
		||||
 | 
			
		||||
The mksyscall.pl script takes the `//sys` and `//sysnb` comments and converts
 | 
			
		||||
them into syscalls. This requires the name of the prototype in the comment to
 | 
			
		||||
match a syscall number in the `zsysnum_${GOOS}_${GOARCH}.go` file. The function
 | 
			
		||||
prototype can be exported (capitalized) or not.
 | 
			
		||||
 | 
			
		||||
Adding a new syscall often just requires adding a new `//sys` function prototype
 | 
			
		||||
with the desired arguments and a capitalized name so it is exported. However, if
 | 
			
		||||
you want the interface to the syscall to be different, often one will make an
 | 
			
		||||
unexported `//sys` prototype, an then write a custom wrapper in
 | 
			
		||||
`syscall_${GOOS}.go`.
 | 
			
		||||
 | 
			
		||||
### types files
 | 
			
		||||
 | 
			
		||||
For each OS, there is a hand-written Go file at `${GOOS}/types.go` (or
 | 
			
		||||
`types_${GOOS}.go` on the old system). This file includes standard C headers and
 | 
			
		||||
creates Go type aliases to the corresponding C types. The file is then fed
 | 
			
		||||
through godef to get the Go compatible definitions. Finally, the generated code
 | 
			
		||||
is fed though mkpost.go to format the code correctly and remove any hidden or
 | 
			
		||||
private identifiers. This cleaned-up code is written to
 | 
			
		||||
`ztypes_${GOOS}_${GOARCH}.go`.
 | 
			
		||||
 | 
			
		||||
The hardest part about preparing this file is figuring out which headers to
 | 
			
		||||
include and which symbols need to be `#define`d to get the actual data
 | 
			
		||||
structures that pass through to the kernel system calls. Some C libraries
 | 
			
		||||
preset alternate versions for binary compatibility and translate them on the
 | 
			
		||||
way in and out of system calls, but there is almost always a `#define` that can
 | 
			
		||||
get the real ones.
 | 
			
		||||
See `types_darwin.go` and `linux/types.go` for examples.
 | 
			
		||||
 | 
			
		||||
To add a new type, add in the necessary include statement at the top of the
 | 
			
		||||
file (if it is not already there) and add in a type alias line. Note that if
 | 
			
		||||
your type is significantly different on different architectures, you may need
 | 
			
		||||
some `#if/#elif` macros in your include statements.
 | 
			
		||||
 | 
			
		||||
### mkerrors.sh
 | 
			
		||||
 | 
			
		||||
This script is used to generate the system's various constants. This doesn't
 | 
			
		||||
just include the error numbers and error strings, but also the signal numbers
 | 
			
		||||
an a wide variety of miscellaneous constants. The constants come from the list
 | 
			
		||||
of include files in the `includes_${uname}` variable. A regex then picks out
 | 
			
		||||
the desired `#define` statements, and generates the corresponding Go constants.
 | 
			
		||||
The error numbers and strings are generated from `#include <errno.h>`, and the
 | 
			
		||||
signal numbers and strings are generated from `#include <signal.h>`. All of
 | 
			
		||||
these constants are written to `zerrors_${GOOS}_${GOARCH}.go` via a C program,
 | 
			
		||||
`_errors.c`, which prints out all the constants.
 | 
			
		||||
 | 
			
		||||
To add a constant, add the header that includes it to the appropriate variable.
 | 
			
		||||
Then, edit the regex (if necessary) to match the desired constant. Avoid making
 | 
			
		||||
the regex too broad to avoid matching unintended constants.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Generated files
 | 
			
		||||
 | 
			
		||||
### `zerror_${GOOS}_${GOARCH}.go`
 | 
			
		||||
 | 
			
		||||
A file containing all of the system's generated error numbers, error strings,
 | 
			
		||||
signal numbers, and constants. Generated by `mkerrors.sh` (see above).
 | 
			
		||||
 | 
			
		||||
### `zsyscall_${GOOS}_${GOARCH}.go`
 | 
			
		||||
 | 
			
		||||
A file containing all the generated syscalls for a specific GOOS and GOARCH.
 | 
			
		||||
Generated by `mksyscall.pl` (see above).
 | 
			
		||||
 | 
			
		||||
### `zsysnum_${GOOS}_${GOARCH}.go`
 | 
			
		||||
 | 
			
		||||
A list of numeric constants for all the syscall number of the specific GOOS
 | 
			
		||||
and GOARCH. Generated by mksysnum (see above).
 | 
			
		||||
 | 
			
		||||
### `ztypes_${GOOS}_${GOARCH}.go`
 | 
			
		||||
 | 
			
		||||
A file containing Go types for passing into (or returning from) syscalls.
 | 
			
		||||
Generated by godefs and the types file (see above).
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
// Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System call support for 386, Darwin
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	JMP	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	JMP	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall9(SB),NOSPLIT,$0-52
 | 
			
		||||
	JMP	syscall·Syscall9(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	JMP	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	JMP	syscall·RawSyscall6(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
// Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System call support for AMD64, Darwin
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	JMP	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	JMP	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall9(SB),NOSPLIT,$0-104
 | 
			
		||||
	JMP	syscall·Syscall9(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	JMP	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	JMP	syscall·RawSyscall6(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
// Copyright 2015 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
// +build arm,darwin
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System call support for ARM, Darwin
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	B	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	B	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall9(SB),NOSPLIT,$0-52
 | 
			
		||||
	B	syscall·Syscall9(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	B	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	B	syscall·RawSyscall6(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
// Copyright 2015 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
// +build arm64,darwin
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System call support for AMD64, Darwin
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	B	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	B	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall9(SB),NOSPLIT,$0-104
 | 
			
		||||
	B	syscall·Syscall9(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	B	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	B	syscall·RawSyscall6(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
// Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System call support for AMD64, DragonFly
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-64
 | 
			
		||||
	JMP	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall6(SB),NOSPLIT,$0-88
 | 
			
		||||
	JMP	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall9(SB),NOSPLIT,$0-112
 | 
			
		||||
	JMP	syscall·Syscall9(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-64
 | 
			
		||||
	JMP	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall6(SB),NOSPLIT,$0-88
 | 
			
		||||
	JMP	syscall·RawSyscall6(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
// Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System call support for 386, FreeBSD
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	JMP	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	JMP	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall9(SB),NOSPLIT,$0-52
 | 
			
		||||
	JMP	syscall·Syscall9(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	JMP	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	JMP	syscall·RawSyscall6(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
// Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System call support for AMD64, FreeBSD
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	JMP	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	JMP	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall9(SB),NOSPLIT,$0-104
 | 
			
		||||
	JMP	syscall·Syscall9(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	JMP	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	JMP	syscall·RawSyscall6(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
// Copyright 2012 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System call support for ARM, FreeBSD
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	B	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	B	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall9(SB),NOSPLIT,$0-52
 | 
			
		||||
	B	syscall·Syscall9(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	B	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	B	syscall·RawSyscall6(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
// Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System calls for 386, Linux
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	JMP	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	JMP	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	JMP	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	JMP	syscall·RawSyscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·socketcall(SB),NOSPLIT,$0-36
 | 
			
		||||
	JMP	syscall·socketcall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·rawsocketcall(SB),NOSPLIT,$0-36
 | 
			
		||||
	JMP	syscall·rawsocketcall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·seek(SB),NOSPLIT,$0-28
 | 
			
		||||
	JMP	syscall·seek(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
// Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System calls for AMD64, Linux
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	JMP	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	JMP	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	JMP	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	JMP	syscall·RawSyscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·gettimeofday(SB),NOSPLIT,$0-16
 | 
			
		||||
	JMP	syscall·gettimeofday(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
// Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System calls for arm, Linux
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	B	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	B	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	B	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	B	syscall·RawSyscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·seek(SB),NOSPLIT,$0-32
 | 
			
		||||
	B	syscall·seek(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
// Copyright 2015 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build linux
 | 
			
		||||
// +build arm64
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	B	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	B	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	B	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	B	syscall·RawSyscall6(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
// Copyright 2015 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build linux
 | 
			
		||||
// +build mips64 mips64le
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System calls for mips64, Linux
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	JMP	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	JMP	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	JMP	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	JMP	syscall·RawSyscall6(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
// Copyright 2016 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build linux
 | 
			
		||||
// +build mips mipsle
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System calls for mips, Linux
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	JMP syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	JMP syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall9(SB),NOSPLIT,$0-52
 | 
			
		||||
	JMP syscall·Syscall9(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	JMP syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	JMP syscall·RawSyscall6(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
// Copyright 2014 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build linux
 | 
			
		||||
// +build ppc64 ppc64le
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System calls for ppc64, Linux
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	BR	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	BR	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	BR	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	BR	syscall·RawSyscall6(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
// Copyright 2016 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build s390x
 | 
			
		||||
// +build linux
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System calls for s390x, Linux
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	BR	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	BR	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	BR	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	BR	syscall·RawSyscall6(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
// Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System call support for 386, NetBSD
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	JMP	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	JMP	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall9(SB),NOSPLIT,$0-52
 | 
			
		||||
	JMP	syscall·Syscall9(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	JMP	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	JMP	syscall·RawSyscall6(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
// Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System call support for AMD64, NetBSD
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	JMP	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	JMP	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall9(SB),NOSPLIT,$0-104
 | 
			
		||||
	JMP	syscall·Syscall9(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	JMP	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	JMP	syscall·RawSyscall6(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
// Copyright 2013 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System call support for ARM, NetBSD
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	B	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	B	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall9(SB),NOSPLIT,$0-52
 | 
			
		||||
	B	syscall·Syscall9(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	B	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	B	syscall·RawSyscall6(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
// Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System call support for 386, OpenBSD
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	JMP	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	JMP	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall9(SB),NOSPLIT,$0-52
 | 
			
		||||
	JMP	syscall·Syscall9(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
 | 
			
		||||
	JMP	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall6(SB),NOSPLIT,$0-40
 | 
			
		||||
	JMP	syscall·RawSyscall6(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
// Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System call support for AMD64, OpenBSD
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Just jump to package syscall's implementation for all these functions.
 | 
			
		||||
// The runtime may know about them.
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	JMP	syscall·Syscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	JMP	syscall·Syscall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·Syscall9(SB),NOSPLIT,$0-104
 | 
			
		||||
	JMP	syscall·Syscall9(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall(SB),NOSPLIT,$0-56
 | 
			
		||||
	JMP	syscall·RawSyscall(SB)
 | 
			
		||||
 | 
			
		||||
TEXT	·RawSyscall6(SB),NOSPLIT,$0-80
 | 
			
		||||
	JMP	syscall·RawSyscall6(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
// Copyright 2014 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !gccgo
 | 
			
		||||
 | 
			
		||||
#include "textflag.h"
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// System calls for amd64, Solaris are implemented in runtime/syscall_solaris.go
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
TEXT ·sysvicall6(SB),NOSPLIT,$0-88
 | 
			
		||||
	JMP	syscall·sysvicall6(SB)
 | 
			
		||||
 | 
			
		||||
TEXT ·rawSysvicall6(SB),NOSPLIT,$0-88
 | 
			
		||||
	JMP	syscall·rawSysvicall6(SB)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
// Copyright 2016 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// Bluetooth sockets and messages
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
// Bluetooth Protocols
 | 
			
		||||
const (
 | 
			
		||||
	BTPROTO_L2CAP  = 0
 | 
			
		||||
	BTPROTO_HCI    = 1
 | 
			
		||||
	BTPROTO_SCO    = 2
 | 
			
		||||
	BTPROTO_RFCOMM = 3
 | 
			
		||||
	BTPROTO_BNEP   = 4
 | 
			
		||||
	BTPROTO_CMTP   = 5
 | 
			
		||||
	BTPROTO_HIDP   = 6
 | 
			
		||||
	BTPROTO_AVDTP  = 7
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	HCI_CHANNEL_RAW     = 0
 | 
			
		||||
	HCI_CHANNEL_USER    = 1
 | 
			
		||||
	HCI_CHANNEL_MONITOR = 2
 | 
			
		||||
	HCI_CHANNEL_CONTROL = 3
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Socketoption Level
 | 
			
		||||
const (
 | 
			
		||||
	SOL_BLUETOOTH = 0x112
 | 
			
		||||
	SOL_HCI       = 0x0
 | 
			
		||||
	SOL_L2CAP     = 0x6
 | 
			
		||||
	SOL_RFCOMM    = 0x12
 | 
			
		||||
	SOL_SCO       = 0x11
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
// Copyright 2015 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	R_OK = 0x4
 | 
			
		||||
	W_OK = 0x2
 | 
			
		||||
	X_OK = 0x1
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,102 @@
 | 
			
		|||
// Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
import "unsafe"
 | 
			
		||||
 | 
			
		||||
// readInt returns the size-bytes unsigned integer in native byte order at offset off.
 | 
			
		||||
func readInt(b []byte, off, size uintptr) (u uint64, ok bool) {
 | 
			
		||||
	if len(b) < int(off+size) {
 | 
			
		||||
		return 0, false
 | 
			
		||||
	}
 | 
			
		||||
	if isBigEndian {
 | 
			
		||||
		return readIntBE(b[off:], size), true
 | 
			
		||||
	}
 | 
			
		||||
	return readIntLE(b[off:], size), true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readIntBE(b []byte, size uintptr) uint64 {
 | 
			
		||||
	switch size {
 | 
			
		||||
	case 1:
 | 
			
		||||
		return uint64(b[0])
 | 
			
		||||
	case 2:
 | 
			
		||||
		_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
 | 
			
		||||
		return uint64(b[1]) | uint64(b[0])<<8
 | 
			
		||||
	case 4:
 | 
			
		||||
		_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
 | 
			
		||||
		return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24
 | 
			
		||||
	case 8:
 | 
			
		||||
		_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
 | 
			
		||||
		return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
 | 
			
		||||
			uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
 | 
			
		||||
	default:
 | 
			
		||||
		panic("syscall: readInt with unsupported size")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readIntLE(b []byte, size uintptr) uint64 {
 | 
			
		||||
	switch size {
 | 
			
		||||
	case 1:
 | 
			
		||||
		return uint64(b[0])
 | 
			
		||||
	case 2:
 | 
			
		||||
		_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
 | 
			
		||||
		return uint64(b[0]) | uint64(b[1])<<8
 | 
			
		||||
	case 4:
 | 
			
		||||
		_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
 | 
			
		||||
		return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24
 | 
			
		||||
	case 8:
 | 
			
		||||
		_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
 | 
			
		||||
		return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
 | 
			
		||||
			uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
 | 
			
		||||
	default:
 | 
			
		||||
		panic("syscall: readInt with unsupported size")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseDirent parses up to max directory entries in buf,
 | 
			
		||||
// appending the names to names. It returns the number of
 | 
			
		||||
// bytes consumed from buf, the number of entries added
 | 
			
		||||
// to names, and the new names slice.
 | 
			
		||||
func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) {
 | 
			
		||||
	origlen := len(buf)
 | 
			
		||||
	count = 0
 | 
			
		||||
	for max != 0 && len(buf) > 0 {
 | 
			
		||||
		reclen, ok := direntReclen(buf)
 | 
			
		||||
		if !ok || reclen > uint64(len(buf)) {
 | 
			
		||||
			return origlen, count, names
 | 
			
		||||
		}
 | 
			
		||||
		rec := buf[:reclen]
 | 
			
		||||
		buf = buf[reclen:]
 | 
			
		||||
		ino, ok := direntIno(rec)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if ino == 0 { // File absent in directory.
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		const namoff = uint64(unsafe.Offsetof(Dirent{}.Name))
 | 
			
		||||
		namlen, ok := direntNamlen(rec)
 | 
			
		||||
		if !ok || namoff+namlen > uint64(len(rec)) {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		name := rec[namoff : namoff+namlen]
 | 
			
		||||
		for i, c := range name {
 | 
			
		||||
			if c == 0 {
 | 
			
		||||
				name = name[:i]
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		// Check for useless names before allocating a string.
 | 
			
		||||
		if string(name) == "." || string(name) == ".." {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		max--
 | 
			
		||||
		count++
 | 
			
		||||
		names = append(names, string(name))
 | 
			
		||||
	}
 | 
			
		||||
	return origlen - len(buf), count, names
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
// Copyright 2016 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
//
 | 
			
		||||
// +build ppc64 s390x mips mips64
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
const isBigEndian = true
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
// Copyright 2016 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
//
 | 
			
		||||
// +build 386 amd64 amd64p32 arm arm64 ppc64le mipsle mips64le
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
const isBigEndian = false
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
// Copyright 2010 The Go Authors.  All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
 | 
			
		||||
 | 
			
		||||
// Unix environment variables.
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
import "syscall"
 | 
			
		||||
 | 
			
		||||
func Getenv(key string) (value string, found bool) {
 | 
			
		||||
	return syscall.Getenv(key)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Setenv(key, value string) error {
 | 
			
		||||
	return syscall.Setenv(key, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Clearenv() {
 | 
			
		||||
	syscall.Clearenv()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Environ() []string {
 | 
			
		||||
	return syscall.Environ()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
// Copyright 2014 The Go Authors.  All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build go1.4
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
import "syscall"
 | 
			
		||||
 | 
			
		||||
func Unsetenv(key string) error {
 | 
			
		||||
	// This was added in Go 1.4.
 | 
			
		||||
	return syscall.Unsetenv(key)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
// +build linux darwin freebsd openbsd netbsd dragonfly
 | 
			
		||||
 | 
			
		||||
// Copyright 2014 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build darwin dragonfly freebsd linux netbsd openbsd
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
import "unsafe"
 | 
			
		||||
 | 
			
		||||
// fcntl64Syscall is usually SYS_FCNTL, but is overridden on 32-bit Linux
 | 
			
		||||
// systems by flock_linux_32bit.go to be SYS_FCNTL64.
 | 
			
		||||
var fcntl64Syscall uintptr = SYS_FCNTL
 | 
			
		||||
 | 
			
		||||
// FcntlFlock performs a fcntl syscall for the F_GETLK, F_SETLK or F_SETLKW command.
 | 
			
		||||
func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error {
 | 
			
		||||
	_, _, errno := Syscall(fcntl64Syscall, fd, uintptr(cmd), uintptr(unsafe.Pointer(lk)))
 | 
			
		||||
	if errno == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return errno
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
// +build linux,386 linux,arm linux,mips linux,mipsle
 | 
			
		||||
 | 
			
		||||
// Copyright 2014 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	// On 32-bit Linux systems, the fcntl syscall that matches Go's
 | 
			
		||||
	// Flock_t type is SYS_FCNTL64, not SYS_FCNTL.
 | 
			
		||||
	fcntl64Syscall = SYS_FCNTL64
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,46 @@
 | 
			
		|||
// Copyright 2015 The Go Authors.  All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build gccgo
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
import "syscall"
 | 
			
		||||
 | 
			
		||||
// We can't use the gc-syntax .s files for gccgo.  On the plus side
 | 
			
		||||
// much of the functionality can be written directly in Go.
 | 
			
		||||
 | 
			
		||||
//extern gccgoRealSyscall
 | 
			
		||||
func realSyscall(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r, errno uintptr)
 | 
			
		||||
 | 
			
		||||
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
 | 
			
		||||
	syscall.Entersyscall()
 | 
			
		||||
	r, errno := realSyscall(trap, a1, a2, a3, 0, 0, 0, 0, 0, 0)
 | 
			
		||||
	syscall.Exitsyscall()
 | 
			
		||||
	return r, 0, syscall.Errno(errno)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno) {
 | 
			
		||||
	syscall.Entersyscall()
 | 
			
		||||
	r, errno := realSyscall(trap, a1, a2, a3, a4, a5, a6, 0, 0, 0)
 | 
			
		||||
	syscall.Exitsyscall()
 | 
			
		||||
	return r, 0, syscall.Errno(errno)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) {
 | 
			
		||||
	syscall.Entersyscall()
 | 
			
		||||
	r, errno := realSyscall(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9)
 | 
			
		||||
	syscall.Exitsyscall()
 | 
			
		||||
	return r, 0, syscall.Errno(errno)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
 | 
			
		||||
	r, errno := realSyscall(trap, a1, a2, a3, 0, 0, 0, 0, 0, 0)
 | 
			
		||||
	return r, 0, syscall.Errno(errno)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno) {
 | 
			
		||||
	r, errno := realSyscall(trap, a1, a2, a3, a4, a5, a6, 0, 0, 0)
 | 
			
		||||
	return r, 0, syscall.Errno(errno)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
// Copyright 2015 The Go Authors.  All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build gccgo
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#define _STRINGIFY2_(x) #x
 | 
			
		||||
#define _STRINGIFY_(x) _STRINGIFY2_(x)
 | 
			
		||||
#define GOSYM_PREFIX _STRINGIFY_(__USER_LABEL_PREFIX__)
 | 
			
		||||
 | 
			
		||||
// Call syscall from C code because the gccgo support for calling from
 | 
			
		||||
// Go to C does not support varargs functions.
 | 
			
		||||
 | 
			
		||||
struct ret {
 | 
			
		||||
	uintptr_t r;
 | 
			
		||||
	uintptr_t err;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ret
 | 
			
		||||
gccgoRealSyscall(uintptr_t trap, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8, uintptr_t a9)
 | 
			
		||||
{
 | 
			
		||||
	struct ret r;
 | 
			
		||||
 | 
			
		||||
	errno = 0;
 | 
			
		||||
	r.r = syscall(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9);
 | 
			
		||||
	r.err = errno;
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Define the use function in C so that it is not inlined.
 | 
			
		||||
 | 
			
		||||
extern void use(void *) __asm__ (GOSYM_PREFIX GOPKGPATH ".use") __attribute__((noinline));
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
use(void *p __attribute__ ((unused)))
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
// Copyright 2015 The Go Authors.  All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build gccgo,linux,amd64
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
import "syscall"
 | 
			
		||||
 | 
			
		||||
//extern gettimeofday
 | 
			
		||||
func realGettimeofday(*Timeval, *byte) int32
 | 
			
		||||
 | 
			
		||||
func gettimeofday(tv *Timeval) (err syscall.Errno) {
 | 
			
		||||
	r := realGettimeofday(tv, nil)
 | 
			
		||||
	if r < 0 {
 | 
			
		||||
		return syscall.GetErrno()
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
// Copyright 2016 The Go Authors.  All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build gccgo,linux,sparc64
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
import "syscall"
 | 
			
		||||
 | 
			
		||||
//extern sysconf
 | 
			
		||||
func realSysconf(name int) int64
 | 
			
		||||
 | 
			
		||||
func sysconf(name int) (n int64, err syscall.Errno) {
 | 
			
		||||
	r := realSysconf(name)
 | 
			
		||||
	if r < 0 {
 | 
			
		||||
		return 0, syscall.GetErrno()
 | 
			
		||||
	}
 | 
			
		||||
	return r, 0
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,179 @@
 | 
			
		|||
#!/usr/bin/env bash
 | 
			
		||||
# Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
# Use of this source code is governed by a BSD-style
 | 
			
		||||
# license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
# This script runs or (given -n) prints suggested commands to generate files for
 | 
			
		||||
# the Architecture/OS specified by the GOARCH and GOOS environment variables.
 | 
			
		||||
# See README.md for more information about how the build system works.
 | 
			
		||||
 | 
			
		||||
GOOSARCH="${GOOS}_${GOARCH}"
 | 
			
		||||
 | 
			
		||||
# defaults
 | 
			
		||||
mksyscall="./mksyscall.pl"
 | 
			
		||||
mkerrors="./mkerrors.sh"
 | 
			
		||||
zerrors="zerrors_$GOOSARCH.go"
 | 
			
		||||
mksysctl=""
 | 
			
		||||
zsysctl="zsysctl_$GOOSARCH.go"
 | 
			
		||||
mksysnum=
 | 
			
		||||
mktypes=
 | 
			
		||||
run="sh"
 | 
			
		||||
cmd=""
 | 
			
		||||
 | 
			
		||||
case "$1" in
 | 
			
		||||
-syscalls)
 | 
			
		||||
	for i in zsyscall*go
 | 
			
		||||
	do
 | 
			
		||||
		# Run the command line that appears in the first line
 | 
			
		||||
		# of the generated file to regenerate it.
 | 
			
		||||
		sed 1q $i | sed 's;^// ;;' | sh > _$i && gofmt < _$i > $i
 | 
			
		||||
		rm _$i
 | 
			
		||||
	done
 | 
			
		||||
	exit 0
 | 
			
		||||
	;;
 | 
			
		||||
-n)
 | 
			
		||||
	run="cat"
 | 
			
		||||
	cmd="echo"
 | 
			
		||||
	shift
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
case "$#" in
 | 
			
		||||
0)
 | 
			
		||||
	;;
 | 
			
		||||
*)
 | 
			
		||||
	echo 'usage: mkall.sh [-n]' 1>&2
 | 
			
		||||
	exit 2
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
if [[ "$GOOS" = "linux" ]] && [[ "$GOARCH" != "sparc64" ]]; then
 | 
			
		||||
	# Use then new build system
 | 
			
		||||
	# Files generated through docker (use $cmd so you can Ctl-C the build or run)
 | 
			
		||||
	$cmd docker build --tag generate:$GOOS $GOOS
 | 
			
		||||
	$cmd docker run --interactive --tty --volume $(dirname "$(readlink -f "$0")"):/build generate:$GOOS
 | 
			
		||||
	exit
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
GOOSARCH_in=syscall_$GOOSARCH.go
 | 
			
		||||
case "$GOOSARCH" in
 | 
			
		||||
_* | *_ | _)
 | 
			
		||||
	echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2
 | 
			
		||||
	exit 1
 | 
			
		||||
	;;
 | 
			
		||||
darwin_386)
 | 
			
		||||
	mkerrors="$mkerrors -m32"
 | 
			
		||||
	mksyscall="./mksyscall.pl -l32"
 | 
			
		||||
	mksysnum="./mksysnum_darwin.pl $(xcrun --show-sdk-path --sdk macosx)/usr/include/sys/syscall.h"
 | 
			
		||||
	mktypes="GOARCH=$GOARCH go tool cgo -godefs"
 | 
			
		||||
	;;
 | 
			
		||||
darwin_amd64)
 | 
			
		||||
	mkerrors="$mkerrors -m64"
 | 
			
		||||
	mksysnum="./mksysnum_darwin.pl $(xcrun --show-sdk-path --sdk macosx)/usr/include/sys/syscall.h"
 | 
			
		||||
	mktypes="GOARCH=$GOARCH go tool cgo -godefs"
 | 
			
		||||
	;;
 | 
			
		||||
darwin_arm)
 | 
			
		||||
	mkerrors="$mkerrors"
 | 
			
		||||
	mksysnum="./mksysnum_darwin.pl /usr/include/sys/syscall.h"
 | 
			
		||||
	mktypes="GOARCH=$GOARCH go tool cgo -godefs"
 | 
			
		||||
	;;
 | 
			
		||||
darwin_arm64)
 | 
			
		||||
	mkerrors="$mkerrors -m64"
 | 
			
		||||
	mksysnum="./mksysnum_darwin.pl $(xcrun --show-sdk-path --sdk iphoneos)/usr/include/sys/syscall.h"
 | 
			
		||||
	mktypes="GOARCH=$GOARCH go tool cgo -godefs"
 | 
			
		||||
	;;
 | 
			
		||||
dragonfly_386)
 | 
			
		||||
	mkerrors="$mkerrors -m32"
 | 
			
		||||
	mksyscall="./mksyscall.pl -l32 -dragonfly"
 | 
			
		||||
	mksysnum="curl -s 'http://gitweb.dragonflybsd.org/dragonfly.git/blob_plain/HEAD:/sys/kern/syscalls.master' | ./mksysnum_dragonfly.pl"
 | 
			
		||||
	mktypes="GOARCH=$GOARCH go tool cgo -godefs"
 | 
			
		||||
	;;
 | 
			
		||||
dragonfly_amd64)
 | 
			
		||||
	mkerrors="$mkerrors -m64"
 | 
			
		||||
	mksyscall="./mksyscall.pl -dragonfly"
 | 
			
		||||
	mksysnum="curl -s 'http://gitweb.dragonflybsd.org/dragonfly.git/blob_plain/HEAD:/sys/kern/syscalls.master' | ./mksysnum_dragonfly.pl"
 | 
			
		||||
	mktypes="GOARCH=$GOARCH go tool cgo -godefs"
 | 
			
		||||
	;;
 | 
			
		||||
freebsd_386)
 | 
			
		||||
	mkerrors="$mkerrors -m32"
 | 
			
		||||
	mksyscall="./mksyscall.pl -l32"
 | 
			
		||||
	mksysnum="curl -s 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl"
 | 
			
		||||
	mktypes="GOARCH=$GOARCH go tool cgo -godefs"
 | 
			
		||||
	;;
 | 
			
		||||
freebsd_amd64)
 | 
			
		||||
	mkerrors="$mkerrors -m64"
 | 
			
		||||
	mksysnum="curl -s 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl"
 | 
			
		||||
	mktypes="GOARCH=$GOARCH go tool cgo -godefs"
 | 
			
		||||
	;;
 | 
			
		||||
freebsd_arm)
 | 
			
		||||
	mkerrors="$mkerrors"
 | 
			
		||||
	mksyscall="./mksyscall.pl -l32 -arm"
 | 
			
		||||
	mksysnum="curl -s 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl"
 | 
			
		||||
	# Let the type of C char be signed for making the bare syscall
 | 
			
		||||
	# API consistent across over platforms.
 | 
			
		||||
	mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
 | 
			
		||||
	;;
 | 
			
		||||
linux_sparc64)
 | 
			
		||||
	GOOSARCH_in=syscall_linux_sparc64.go
 | 
			
		||||
	unistd_h=/usr/include/sparc64-linux-gnu/asm/unistd.h
 | 
			
		||||
	mkerrors="$mkerrors -m64"
 | 
			
		||||
	mksysnum="./mksysnum_linux.pl $unistd_h"
 | 
			
		||||
	mktypes="GOARCH=$GOARCH go tool cgo -godefs"
 | 
			
		||||
	;;
 | 
			
		||||
netbsd_386)
 | 
			
		||||
	mkerrors="$mkerrors -m32"
 | 
			
		||||
	mksyscall="./mksyscall.pl -l32 -netbsd"
 | 
			
		||||
	mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl"
 | 
			
		||||
	mktypes="GOARCH=$GOARCH go tool cgo -godefs"
 | 
			
		||||
	;;
 | 
			
		||||
netbsd_amd64)
 | 
			
		||||
	mkerrors="$mkerrors -m64"
 | 
			
		||||
	mksyscall="./mksyscall.pl -netbsd"
 | 
			
		||||
	mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl"
 | 
			
		||||
	mktypes="GOARCH=$GOARCH go tool cgo -godefs"
 | 
			
		||||
	;;
 | 
			
		||||
openbsd_386)
 | 
			
		||||
	mkerrors="$mkerrors -m32"
 | 
			
		||||
	mksyscall="./mksyscall.pl -l32 -openbsd"
 | 
			
		||||
	mksysctl="./mksysctl_openbsd.pl"
 | 
			
		||||
	zsysctl="zsysctl_openbsd.go"
 | 
			
		||||
	mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl"
 | 
			
		||||
	mktypes="GOARCH=$GOARCH go tool cgo -godefs"
 | 
			
		||||
	;;
 | 
			
		||||
openbsd_amd64)
 | 
			
		||||
	mkerrors="$mkerrors -m64"
 | 
			
		||||
	mksyscall="./mksyscall.pl -openbsd"
 | 
			
		||||
	mksysctl="./mksysctl_openbsd.pl"
 | 
			
		||||
	zsysctl="zsysctl_openbsd.go"
 | 
			
		||||
	mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl"
 | 
			
		||||
	mktypes="GOARCH=$GOARCH go tool cgo -godefs"
 | 
			
		||||
	;;
 | 
			
		||||
solaris_amd64)
 | 
			
		||||
	mksyscall="./mksyscall_solaris.pl"
 | 
			
		||||
	mkerrors="$mkerrors -m64"
 | 
			
		||||
	mksysnum=
 | 
			
		||||
	mktypes="GOARCH=$GOARCH go tool cgo -godefs"
 | 
			
		||||
	;;
 | 
			
		||||
*)
 | 
			
		||||
	echo 'unrecognized $GOOS_$GOARCH: ' "$GOOSARCH" 1>&2
 | 
			
		||||
	exit 1
 | 
			
		||||
	;;
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
(
 | 
			
		||||
	if [ -n "$mkerrors" ]; then echo "$mkerrors |gofmt >$zerrors"; fi
 | 
			
		||||
	case "$GOOS" in
 | 
			
		||||
	*)
 | 
			
		||||
		syscall_goos="syscall_$GOOS.go"
 | 
			
		||||
		case "$GOOS" in
 | 
			
		||||
		darwin | dragonfly | freebsd | netbsd | openbsd)
 | 
			
		||||
			syscall_goos="syscall_bsd.go $syscall_goos"
 | 
			
		||||
			;;
 | 
			
		||||
		esac
 | 
			
		||||
		if [ -n "$mksyscall" ]; then echo "$mksyscall -tags $GOOS,$GOARCH $syscall_goos $GOOSARCH_in |gofmt >zsyscall_$GOOSARCH.go"; fi
 | 
			
		||||
		;;
 | 
			
		||||
	esac
 | 
			
		||||
	if [ -n "$mksysctl" ]; then echo "$mksysctl |gofmt >$zsysctl"; fi
 | 
			
		||||
	if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi
 | 
			
		||||
	if [ -n "$mktypes" ]; then
 | 
			
		||||
		echo "$mktypes types_$GOOS.go | go run mkpost.go > ztypes_$GOOSARCH.go";
 | 
			
		||||
	fi
 | 
			
		||||
) | $run
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,549 @@
 | 
			
		|||
#!/usr/bin/env bash
 | 
			
		||||
# Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
# Use of this source code is governed by a BSD-style
 | 
			
		||||
# license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
# Generate Go code listing errors and other #defined constant
 | 
			
		||||
# values (ENAMETOOLONG etc.), by asking the preprocessor
 | 
			
		||||
# about the definitions.
 | 
			
		||||
 | 
			
		||||
unset LANG
 | 
			
		||||
export LC_ALL=C
 | 
			
		||||
export LC_CTYPE=C
 | 
			
		||||
 | 
			
		||||
if test -z "$GOARCH" -o -z "$GOOS"; then
 | 
			
		||||
	echo 1>&2 "GOARCH or GOOS not defined in environment"
 | 
			
		||||
	exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Check that we are using the new build system if we should
 | 
			
		||||
if [[ "$GOOS" -eq "linux" ]] && [[ "$GOARCH" != "sparc64" ]]; then
 | 
			
		||||
	if [[ "$GOLANG_SYS_BUILD" -ne "docker" ]]; then
 | 
			
		||||
		echo 1>&2 "In the new build system, mkerrors should not be called directly."
 | 
			
		||||
		echo 1>&2 "See README.md"
 | 
			
		||||
		exit 1
 | 
			
		||||
	fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
CC=${CC:-cc}
 | 
			
		||||
 | 
			
		||||
if [[ "$GOOS" -eq "solaris" ]]; then
 | 
			
		||||
	# Assumes GNU versions of utilities in PATH.
 | 
			
		||||
	export PATH=/usr/gnu/bin:$PATH
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
uname=$(uname)
 | 
			
		||||
 | 
			
		||||
includes_Darwin='
 | 
			
		||||
#define _DARWIN_C_SOURCE
 | 
			
		||||
#define KERNEL
 | 
			
		||||
#define _DARWIN_USE_64_BIT_INODE
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/event.h>
 | 
			
		||||
#include <sys/ptrace.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/sockio.h>
 | 
			
		||||
#include <sys/sysctl.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
#include <net/bpf.h>
 | 
			
		||||
#include <net/if.h>
 | 
			
		||||
#include <net/if_types.h>
 | 
			
		||||
#include <net/route.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <netinet/ip.h>
 | 
			
		||||
#include <termios.h>
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
includes_DragonFly='
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/event.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/sockio.h>
 | 
			
		||||
#include <sys/sysctl.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <net/bpf.h>
 | 
			
		||||
#include <net/if.h>
 | 
			
		||||
#include <net/if_types.h>
 | 
			
		||||
#include <net/route.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <termios.h>
 | 
			
		||||
#include <netinet/ip.h>
 | 
			
		||||
#include <net/ip_mroute/ip_mroute.h>
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
includes_FreeBSD='
 | 
			
		||||
#include <sys/param.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/event.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/sockio.h>
 | 
			
		||||
#include <sys/sysctl.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <net/bpf.h>
 | 
			
		||||
#include <net/if.h>
 | 
			
		||||
#include <net/if_types.h>
 | 
			
		||||
#include <net/route.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <termios.h>
 | 
			
		||||
#include <netinet/ip.h>
 | 
			
		||||
#include <netinet/ip_mroute.h>
 | 
			
		||||
#include <sys/extattr.h>
 | 
			
		||||
 | 
			
		||||
#if __FreeBSD__ >= 10
 | 
			
		||||
#define IFT_CARP	0xf8	// IFT_CARP is deprecated in FreeBSD 10
 | 
			
		||||
#undef SIOCAIFADDR
 | 
			
		||||
#define SIOCAIFADDR	_IOW(105, 26, struct oifaliasreq)	// ifaliasreq contains if_data
 | 
			
		||||
#undef SIOCSIFPHYADDR
 | 
			
		||||
#define SIOCSIFPHYADDR	_IOW(105, 70, struct oifaliasreq)	// ifaliasreq contains if_data
 | 
			
		||||
#endif
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
includes_Linux='
 | 
			
		||||
#define _LARGEFILE_SOURCE
 | 
			
		||||
#define _LARGEFILE64_SOURCE
 | 
			
		||||
#ifndef __LP64__
 | 
			
		||||
#define _FILE_OFFSET_BITS 64
 | 
			
		||||
#endif
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
 | 
			
		||||
// <sys/ioctl.h> is broken on powerpc64, as it fails to include definitions of
 | 
			
		||||
// these structures. We just include them copied from <bits/termios.h>.
 | 
			
		||||
#if defined(__powerpc__)
 | 
			
		||||
struct sgttyb {
 | 
			
		||||
        char    sg_ispeed;
 | 
			
		||||
        char    sg_ospeed;
 | 
			
		||||
        char    sg_erase;
 | 
			
		||||
        char    sg_kill;
 | 
			
		||||
        short   sg_flags;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct tchars {
 | 
			
		||||
        char    t_intrc;
 | 
			
		||||
        char    t_quitc;
 | 
			
		||||
        char    t_startc;
 | 
			
		||||
        char    t_stopc;
 | 
			
		||||
        char    t_eofc;
 | 
			
		||||
        char    t_brkc;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ltchars {
 | 
			
		||||
        char    t_suspc;
 | 
			
		||||
        char    t_dsuspc;
 | 
			
		||||
        char    t_rprntc;
 | 
			
		||||
        char    t_flushc;
 | 
			
		||||
        char    t_werasc;
 | 
			
		||||
        char    t_lnextc;
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <bits/sockaddr.h>
 | 
			
		||||
#include <sys/epoll.h>
 | 
			
		||||
#include <sys/eventfd.h>
 | 
			
		||||
#include <sys/inotify.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#include <sys/mount.h>
 | 
			
		||||
#include <sys/prctl.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/time.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <linux/if.h>
 | 
			
		||||
#include <linux/if_alg.h>
 | 
			
		||||
#include <linux/if_arp.h>
 | 
			
		||||
#include <linux/if_ether.h>
 | 
			
		||||
#include <linux/if_tun.h>
 | 
			
		||||
#include <linux/if_packet.h>
 | 
			
		||||
#include <linux/if_addr.h>
 | 
			
		||||
#include <linux/falloc.h>
 | 
			
		||||
#include <linux/filter.h>
 | 
			
		||||
#include <linux/fs.h>
 | 
			
		||||
#include <linux/keyctl.h>
 | 
			
		||||
#include <linux/netlink.h>
 | 
			
		||||
#include <linux/random.h>
 | 
			
		||||
#include <linux/reboot.h>
 | 
			
		||||
#include <linux/rtnetlink.h>
 | 
			
		||||
#include <linux/ptrace.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/wait.h>
 | 
			
		||||
#include <linux/icmpv6.h>
 | 
			
		||||
#include <linux/serial.h>
 | 
			
		||||
#include <linux/can.h>
 | 
			
		||||
#include <linux/vm_sockets.h>
 | 
			
		||||
#include <net/route.h>
 | 
			
		||||
#include <asm/termbits.h>
 | 
			
		||||
 | 
			
		||||
#ifndef MSG_FASTOPEN
 | 
			
		||||
#define MSG_FASTOPEN    0x20000000
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef PTRACE_GETREGS
 | 
			
		||||
#define PTRACE_GETREGS	0xc
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef PTRACE_SETREGS
 | 
			
		||||
#define PTRACE_SETREGS	0xd
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef SOL_NETLINK
 | 
			
		||||
#define SOL_NETLINK	270
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef SOL_BLUETOOTH
 | 
			
		||||
// SPARC includes this in /usr/include/sparc64-linux-gnu/bits/socket.h
 | 
			
		||||
// but it is already in bluetooth_linux.go
 | 
			
		||||
#undef SOL_BLUETOOTH
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Certain constants are missing from the fs/crypto UAPI
 | 
			
		||||
#define FS_KEY_DESC_PREFIX              "fscrypt:"
 | 
			
		||||
#define FS_KEY_DESC_PREFIX_SIZE         8
 | 
			
		||||
#define FS_MAX_KEY_SIZE                 64
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
includes_NetBSD='
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/param.h>
 | 
			
		||||
#include <sys/event.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/sockio.h>
 | 
			
		||||
#include <sys/sysctl.h>
 | 
			
		||||
#include <sys/termios.h>
 | 
			
		||||
#include <sys/ttycom.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
#include <net/bpf.h>
 | 
			
		||||
#include <net/if.h>
 | 
			
		||||
#include <net/if_types.h>
 | 
			
		||||
#include <net/route.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <netinet/in_systm.h>
 | 
			
		||||
#include <netinet/ip.h>
 | 
			
		||||
#include <netinet/ip_mroute.h>
 | 
			
		||||
#include <netinet/if_ether.h>
 | 
			
		||||
 | 
			
		||||
// Needed since <sys/param.h> refers to it...
 | 
			
		||||
#define schedppq 1
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
includes_OpenBSD='
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/param.h>
 | 
			
		||||
#include <sys/event.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/sockio.h>
 | 
			
		||||
#include <sys/sysctl.h>
 | 
			
		||||
#include <sys/termios.h>
 | 
			
		||||
#include <sys/ttycom.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
#include <net/bpf.h>
 | 
			
		||||
#include <net/if.h>
 | 
			
		||||
#include <net/if_types.h>
 | 
			
		||||
#include <net/if_var.h>
 | 
			
		||||
#include <net/route.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <netinet/in_systm.h>
 | 
			
		||||
#include <netinet/ip.h>
 | 
			
		||||
#include <netinet/ip_mroute.h>
 | 
			
		||||
#include <netinet/if_ether.h>
 | 
			
		||||
#include <net/if_bridge.h>
 | 
			
		||||
 | 
			
		||||
// We keep some constants not supported in OpenBSD 5.5 and beyond for
 | 
			
		||||
// the promise of compatibility.
 | 
			
		||||
#define EMUL_ENABLED		0x1
 | 
			
		||||
#define EMUL_NATIVE		0x2
 | 
			
		||||
#define IPV6_FAITH		0x1d
 | 
			
		||||
#define IPV6_OPTIONS		0x1
 | 
			
		||||
#define IPV6_RTHDR_STRICT	0x1
 | 
			
		||||
#define IPV6_SOCKOPT_RESERVED1	0x3
 | 
			
		||||
#define SIOCGIFGENERIC		0xc020693a
 | 
			
		||||
#define SIOCSIFGENERIC		0x80206939
 | 
			
		||||
#define WALTSIG			0x4
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
includes_SunOS='
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/sockio.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <net/bpf.h>
 | 
			
		||||
#include <net/if.h>
 | 
			
		||||
#include <net/if_arp.h>
 | 
			
		||||
#include <net/if_types.h>
 | 
			
		||||
#include <net/route.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <termios.h>
 | 
			
		||||
#include <netinet/ip.h>
 | 
			
		||||
#include <netinet/ip_mroute.h>
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
includes='
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/file.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <netinet/ip.h>
 | 
			
		||||
#include <netinet/ip6.h>
 | 
			
		||||
#include <netinet/tcp.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <sys/signal.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <sys/resource.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
'
 | 
			
		||||
ccflags="$@"
 | 
			
		||||
 | 
			
		||||
# Write go tool cgo -godefs input.
 | 
			
		||||
(
 | 
			
		||||
	echo package unix
 | 
			
		||||
	echo
 | 
			
		||||
	echo '/*'
 | 
			
		||||
	indirect="includes_$(uname)"
 | 
			
		||||
	echo "${!indirect} $includes"
 | 
			
		||||
	echo '*/'
 | 
			
		||||
	echo 'import "C"'
 | 
			
		||||
	echo 'import "syscall"'
 | 
			
		||||
	echo
 | 
			
		||||
	echo 'const ('
 | 
			
		||||
 | 
			
		||||
	# The gcc command line prints all the #defines
 | 
			
		||||
	# it encounters while processing the input
 | 
			
		||||
	echo "${!indirect} $includes" | $CC -x c - -E -dM $ccflags |
 | 
			
		||||
	awk '
 | 
			
		||||
		$1 != "#define" || $2 ~ /\(/ || $3 == "" {next}
 | 
			
		||||
 | 
			
		||||
		$2 ~ /^E([ABCD]X|[BIS]P|[SD]I|S|FL)$/ {next}  # 386 registers
 | 
			
		||||
		$2 ~ /^(SIGEV_|SIGSTKSZ|SIGRT(MIN|MAX))/ {next}
 | 
			
		||||
		$2 ~ /^(SCM_SRCRT)$/ {next}
 | 
			
		||||
		$2 ~ /^(MAP_FAILED)$/ {next}
 | 
			
		||||
		$2 ~ /^ELF_.*$/ {next}# <asm/elf.h> contains ELF_ARCH, etc.
 | 
			
		||||
 | 
			
		||||
		$2 ~ /^EXTATTR_NAMESPACE_NAMES/ ||
 | 
			
		||||
		$2 ~ /^EXTATTR_NAMESPACE_[A-Z]+_STRING/ {next}
 | 
			
		||||
 | 
			
		||||
		$2 !~ /^ETH_/ &&
 | 
			
		||||
		$2 !~ /^EPROC_/ &&
 | 
			
		||||
		$2 !~ /^EQUIV_/ &&
 | 
			
		||||
		$2 !~ /^EXPR_/ &&
 | 
			
		||||
		$2 ~ /^E[A-Z0-9_]+$/ ||
 | 
			
		||||
		$2 ~ /^B[0-9_]+$/ ||
 | 
			
		||||
		$2 == "BOTHER" ||
 | 
			
		||||
		$2 ~ /^CI?BAUD(EX)?$/ ||
 | 
			
		||||
		$2 == "IBSHIFT" ||
 | 
			
		||||
		$2 ~ /^V[A-Z0-9]+$/ ||
 | 
			
		||||
		$2 ~ /^CS[A-Z0-9]/ ||
 | 
			
		||||
		$2 ~ /^I(SIG|CANON|CRNL|UCLC|EXTEN|MAXBEL|STRIP|UTF8)$/ ||
 | 
			
		||||
		$2 ~ /^IGN/ ||
 | 
			
		||||
		$2 ~ /^IX(ON|ANY|OFF)$/ ||
 | 
			
		||||
		$2 ~ /^IN(LCR|PCK)$/ ||
 | 
			
		||||
		$2 ~ /(^FLU?SH)|(FLU?SH$)/ ||
 | 
			
		||||
		$2 ~ /^C(LOCAL|READ|MSPAR|RTSCTS)$/ ||
 | 
			
		||||
		$2 == "BRKINT" ||
 | 
			
		||||
		$2 == "HUPCL" ||
 | 
			
		||||
		$2 == "PENDIN" ||
 | 
			
		||||
		$2 == "TOSTOP" ||
 | 
			
		||||
		$2 == "XCASE" ||
 | 
			
		||||
		$2 == "ALTWERASE" ||
 | 
			
		||||
		$2 == "NOKERNINFO" ||
 | 
			
		||||
		$2 ~ /^PAR/ ||
 | 
			
		||||
		$2 ~ /^SIG[^_]/ ||
 | 
			
		||||
		$2 ~ /^O[CNPFPL][A-Z]+[^_][A-Z]+$/ ||
 | 
			
		||||
		$2 ~ /^(NL|CR|TAB|BS|VT|FF)DLY$/ ||
 | 
			
		||||
		$2 ~ /^(NL|CR|TAB|BS|VT|FF)[0-9]$/ ||
 | 
			
		||||
		$2 ~ /^O?XTABS$/ ||
 | 
			
		||||
		$2 ~ /^TC[IO](ON|OFF)$/ ||
 | 
			
		||||
		$2 ~ /^IN_/ ||
 | 
			
		||||
		$2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
 | 
			
		||||
		$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|ICMP6|TCP|EVFILT|NOTE|EV|SHUT|PROT|MAP|PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ ||
 | 
			
		||||
		$2 ~ /^FALLOC_/ ||
 | 
			
		||||
		$2 == "ICMPV6_FILTER" ||
 | 
			
		||||
		$2 == "SOMAXCONN" ||
 | 
			
		||||
		$2 == "NAME_MAX" ||
 | 
			
		||||
		$2 == "IFNAMSIZ" ||
 | 
			
		||||
		$2 ~ /^CTL_(MAXNAME|NET|QUERY)$/ ||
 | 
			
		||||
		$2 ~ /^SYSCTL_VERS/ ||
 | 
			
		||||
		$2 ~ /^(MS|MNT)_/ ||
 | 
			
		||||
		$2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ ||
 | 
			
		||||
		$2 ~ /^(O|F|E?FD|NAME|S|PTRACE|PT)_/ ||
 | 
			
		||||
		$2 ~ /^LINUX_REBOOT_CMD_/ ||
 | 
			
		||||
		$2 ~ /^LINUX_REBOOT_MAGIC[12]$/ ||
 | 
			
		||||
		$2 !~ "NLA_TYPE_MASK" &&
 | 
			
		||||
		$2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P)_/ ||
 | 
			
		||||
		$2 ~ /^SIOC/ ||
 | 
			
		||||
		$2 ~ /^TIOC/ ||
 | 
			
		||||
		$2 ~ /^TCGET/ ||
 | 
			
		||||
		$2 ~ /^TCSET/ ||
 | 
			
		||||
		$2 ~ /^TC(FLSH|SBRKP?|XONC)$/ ||
 | 
			
		||||
		$2 !~ "RTF_BITS" &&
 | 
			
		||||
		$2 ~ /^(IFF|IFT|NET_RT|RTM|RTF|RTV|RTA|RTAX)_/ ||
 | 
			
		||||
		$2 ~ /^BIOC/ ||
 | 
			
		||||
		$2 ~ /^RUSAGE_(SELF|CHILDREN|THREAD)/ ||
 | 
			
		||||
		$2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|LOCKS|MEMLOCK|MSGQUEUE|NICE|NOFILE|NPROC|RSS|RTPRIO|RTTIME|SIGPENDING|STACK)|RLIM_INFINITY/ ||
 | 
			
		||||
		$2 ~ /^PRIO_(PROCESS|PGRP|USER)/ ||
 | 
			
		||||
		$2 ~ /^CLONE_[A-Z_]+/ ||
 | 
			
		||||
		$2 !~ /^(BPF_TIMEVAL)$/ &&
 | 
			
		||||
		$2 ~ /^(BPF|DLT)_/ ||
 | 
			
		||||
		$2 ~ /^CLOCK_/ ||
 | 
			
		||||
		$2 ~ /^CAN_/ ||
 | 
			
		||||
		$2 ~ /^ALG_/ ||
 | 
			
		||||
		$2 ~ /^FS_(POLICY_FLAGS|KEY_DESC|ENCRYPTION_MODE|[A-Z0-9_]+_KEY_SIZE|IOC_(GET|SET)_ENCRYPTION)/ ||
 | 
			
		||||
		$2 ~ /^GRND_/ ||
 | 
			
		||||
		$2 ~ /^KEY_(SPEC|REQKEY_DEFL)_/ ||
 | 
			
		||||
		$2 ~ /^KEYCTL_/ ||
 | 
			
		||||
		$2 ~ /^SPLICE_/ ||
 | 
			
		||||
		$2 ~ /^(VM|VMADDR)_/ ||
 | 
			
		||||
		$2 !~ "WMESGLEN" &&
 | 
			
		||||
		$2 ~ /^W[A-Z0-9]+$/ ||
 | 
			
		||||
		$2 ~ /^BLK[A-Z]*(GET$|SET$|BUF$|PART$|SIZE)/ {printf("\t%s = C.%s\n", $2, $2)}
 | 
			
		||||
		$2 ~ /^__WCOREFLAG$/ {next}
 | 
			
		||||
		$2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)}
 | 
			
		||||
 | 
			
		||||
		{next}
 | 
			
		||||
	' | sort
 | 
			
		||||
 | 
			
		||||
	echo ')'
 | 
			
		||||
) >_const.go
 | 
			
		||||
 | 
			
		||||
# Pull out the error names for later.
 | 
			
		||||
errors=$(
 | 
			
		||||
	echo '#include <errno.h>' | $CC -x c - -E -dM $ccflags |
 | 
			
		||||
	awk '$1=="#define" && $2 ~ /^E[A-Z0-9_]+$/ { print $2 }' |
 | 
			
		||||
	sort
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# Pull out the signal names for later.
 | 
			
		||||
signals=$(
 | 
			
		||||
	echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags |
 | 
			
		||||
	awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print $2 }' |
 | 
			
		||||
	egrep -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT)' |
 | 
			
		||||
	sort
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# Again, writing regexps to a file.
 | 
			
		||||
echo '#include <errno.h>' | $CC -x c - -E -dM $ccflags |
 | 
			
		||||
	awk '$1=="#define" && $2 ~ /^E[A-Z0-9_]+$/ { print "^\t" $2 "[ \t]*=" }' |
 | 
			
		||||
	sort >_error.grep
 | 
			
		||||
echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags |
 | 
			
		||||
	awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print "^\t" $2 "[ \t]*=" }' |
 | 
			
		||||
	egrep -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT)' |
 | 
			
		||||
	sort >_signal.grep
 | 
			
		||||
 | 
			
		||||
echo '// mkerrors.sh' "$@"
 | 
			
		||||
echo '// Code generated by the command above; see README.md. DO NOT EDIT.'
 | 
			
		||||
echo
 | 
			
		||||
echo "// +build ${GOARCH},${GOOS}"
 | 
			
		||||
echo
 | 
			
		||||
go tool cgo -godefs -- "$@" _const.go >_error.out
 | 
			
		||||
cat _error.out | grep -vf _error.grep | grep -vf _signal.grep
 | 
			
		||||
echo
 | 
			
		||||
echo '// Errors'
 | 
			
		||||
echo 'const ('
 | 
			
		||||
cat _error.out | grep -f _error.grep | sed 's/=\(.*\)/= syscall.Errno(\1)/'
 | 
			
		||||
echo ')'
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
echo '// Signals'
 | 
			
		||||
echo 'const ('
 | 
			
		||||
cat _error.out | grep -f _signal.grep | sed 's/=\(.*\)/= syscall.Signal(\1)/'
 | 
			
		||||
echo ')'
 | 
			
		||||
 | 
			
		||||
# Run C program to print error and syscall strings.
 | 
			
		||||
(
 | 
			
		||||
	echo -E "
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
 | 
			
		||||
#define nelem(x) (sizeof(x)/sizeof((x)[0]))
 | 
			
		||||
 | 
			
		||||
enum { A = 'A', Z = 'Z', a = 'a', z = 'z' }; // avoid need for single quotes below
 | 
			
		||||
 | 
			
		||||
int errors[] = {
 | 
			
		||||
"
 | 
			
		||||
	for i in $errors
 | 
			
		||||
	do
 | 
			
		||||
		echo -E '	'$i,
 | 
			
		||||
	done
 | 
			
		||||
 | 
			
		||||
	echo -E "
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int signals[] = {
 | 
			
		||||
"
 | 
			
		||||
	for i in $signals
 | 
			
		||||
	do
 | 
			
		||||
		echo -E '	'$i,
 | 
			
		||||
	done
 | 
			
		||||
 | 
			
		||||
	# Use -E because on some systems bash builtin interprets \n itself.
 | 
			
		||||
	echo -E '
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
intcmp(const void *a, const void *b)
 | 
			
		||||
{
 | 
			
		||||
	return *(int*)a - *(int*)b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
main(void)
 | 
			
		||||
{
 | 
			
		||||
	int i, e;
 | 
			
		||||
	char buf[1024], *p;
 | 
			
		||||
 | 
			
		||||
	printf("\n\n// Error table\n");
 | 
			
		||||
	printf("var errors = [...]string {\n");
 | 
			
		||||
	qsort(errors, nelem(errors), sizeof errors[0], intcmp);
 | 
			
		||||
	for(i=0; i<nelem(errors); i++) {
 | 
			
		||||
		e = errors[i];
 | 
			
		||||
		if(i > 0 && errors[i-1] == e)
 | 
			
		||||
			continue;
 | 
			
		||||
		strcpy(buf, strerror(e));
 | 
			
		||||
		// lowercase first letter: Bad -> bad, but STREAM -> STREAM.
 | 
			
		||||
		if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z)
 | 
			
		||||
			buf[0] += a - A;
 | 
			
		||||
		printf("\t%d: \"%s\",\n", e, buf);
 | 
			
		||||
	}
 | 
			
		||||
	printf("}\n\n");
 | 
			
		||||
 | 
			
		||||
	printf("\n\n// Signal table\n");
 | 
			
		||||
	printf("var signals = [...]string {\n");
 | 
			
		||||
	qsort(signals, nelem(signals), sizeof signals[0], intcmp);
 | 
			
		||||
	for(i=0; i<nelem(signals); i++) {
 | 
			
		||||
		e = signals[i];
 | 
			
		||||
		if(i > 0 && signals[i-1] == e)
 | 
			
		||||
			continue;
 | 
			
		||||
		strcpy(buf, strsignal(e));
 | 
			
		||||
		// lowercase first letter: Bad -> bad, but STREAM -> STREAM.
 | 
			
		||||
		if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z)
 | 
			
		||||
			buf[0] += a - A;
 | 
			
		||||
		// cut trailing : number.
 | 
			
		||||
		p = strrchr(buf, ":"[0]);
 | 
			
		||||
		if(p)
 | 
			
		||||
			*p = '\0';
 | 
			
		||||
		printf("\t%d: \"%s\",\n", e, buf);
 | 
			
		||||
	}
 | 
			
		||||
	printf("}\n\n");
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
'
 | 
			
		||||
) >_errors.c
 | 
			
		||||
 | 
			
		||||
$CC $ccflags -o _errors _errors.c && $GORUN ./_errors && rm -f _errors.c _errors _const.go _error.grep _signal.grep _error.out
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,88 @@
 | 
			
		|||
// Copyright 2016 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build ignore
 | 
			
		||||
 | 
			
		||||
// mkpost processes the output of cgo -godefs to
 | 
			
		||||
// modify the generated types. It is used to clean up
 | 
			
		||||
// the sys API in an architecture specific manner.
 | 
			
		||||
//
 | 
			
		||||
// mkpost is run after cgo -godefs; see README.md.
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/format"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"regexp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	// Get the OS and architecture (using GOARCH_TARGET if it exists)
 | 
			
		||||
	goos := os.Getenv("GOOS")
 | 
			
		||||
	goarch := os.Getenv("GOARCH_TARGET")
 | 
			
		||||
	if goarch == "" {
 | 
			
		||||
		goarch = os.Getenv("GOARCH")
 | 
			
		||||
	}
 | 
			
		||||
	// Check that we are using the new build system if we should be.
 | 
			
		||||
	if goos == "linux" && goarch != "sparc64" {
 | 
			
		||||
		if os.Getenv("GOLANG_SYS_BUILD") != "docker" {
 | 
			
		||||
			os.Stderr.WriteString("In the new build system, mkpost should not be called directly.\n")
 | 
			
		||||
			os.Stderr.WriteString("See README.md\n")
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b, err := ioutil.ReadAll(os.Stdin)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we have empty Ptrace structs, we should delete them. Only s390x emits
 | 
			
		||||
	// nonempty Ptrace structs.
 | 
			
		||||
	ptraceRexexp := regexp.MustCompile(`type Ptrace((Psw|Fpregs|Per) struct {\s*})`)
 | 
			
		||||
	b = ptraceRexexp.ReplaceAll(b, nil)
 | 
			
		||||
 | 
			
		||||
	// Replace the control_regs union with a blank identifier for now.
 | 
			
		||||
	controlRegsRegex := regexp.MustCompile(`(Control_regs)\s+\[0\]uint64`)
 | 
			
		||||
	b = controlRegsRegex.ReplaceAll(b, []byte("_ [0]uint64"))
 | 
			
		||||
 | 
			
		||||
	// Remove fields that are added by glibc
 | 
			
		||||
	// Note that this is unstable as the identifers are private.
 | 
			
		||||
	removeFieldsRegex := regexp.MustCompile(`X__glibc\S*`)
 | 
			
		||||
	b = removeFieldsRegex.ReplaceAll(b, []byte("_"))
 | 
			
		||||
 | 
			
		||||
	// We refuse to export private fields on s390x
 | 
			
		||||
	if goarch == "s390x" && goos == "linux" {
 | 
			
		||||
		// Remove cgo padding fields
 | 
			
		||||
		removeFieldsRegex := regexp.MustCompile(`Pad_cgo_\d+`)
 | 
			
		||||
		b = removeFieldsRegex.ReplaceAll(b, []byte("_"))
 | 
			
		||||
 | 
			
		||||
		// Remove padding, hidden, or unused fields
 | 
			
		||||
		removeFieldsRegex = regexp.MustCompile(`X_\S+`)
 | 
			
		||||
		b = removeFieldsRegex.ReplaceAll(b, []byte("_"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Remove the first line of warning from cgo
 | 
			
		||||
	b = b[bytes.IndexByte(b, '\n')+1:]
 | 
			
		||||
	// Modify the command in the header to include:
 | 
			
		||||
	//  mkpost, our own warning, and a build tag.
 | 
			
		||||
	replacement := fmt.Sprintf(`$1 | go run mkpost.go
 | 
			
		||||
// Code generated by the command above; see README.md. DO NOT EDIT.
 | 
			
		||||
 | 
			
		||||
// +build %s,%s`, goarch, goos)
 | 
			
		||||
	cgoCommandRegex := regexp.MustCompile(`(cgo -godefs .*)`)
 | 
			
		||||
	b = cgoCommandRegex.ReplaceAll(b, []byte(replacement))
 | 
			
		||||
 | 
			
		||||
	// gofmt
 | 
			
		||||
	b, err = format.Source(b)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	os.Stdout.Write(b)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,328 @@
 | 
			
		|||
#!/usr/bin/env perl
 | 
			
		||||
# Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
# Use of this source code is governed by a BSD-style
 | 
			
		||||
# license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
# This program reads a file containing function prototypes
 | 
			
		||||
# (like syscall_darwin.go) and generates system call bodies.
 | 
			
		||||
# The prototypes are marked by lines beginning with "//sys"
 | 
			
		||||
# and read like func declarations if //sys is replaced by func, but:
 | 
			
		||||
#	* The parameter lists must give a name for each argument.
 | 
			
		||||
#	  This includes return parameters.
 | 
			
		||||
#	* The parameter lists must give a type for each argument:
 | 
			
		||||
#	  the (x, y, z int) shorthand is not allowed.
 | 
			
		||||
#	* If the return parameter is an error number, it must be named errno.
 | 
			
		||||
 | 
			
		||||
# A line beginning with //sysnb is like //sys, except that the
 | 
			
		||||
# goroutine will not be suspended during the execution of the system
 | 
			
		||||
# call.  This must only be used for system calls which can never
 | 
			
		||||
# block, as otherwise the system call could cause all goroutines to
 | 
			
		||||
# hang.
 | 
			
		||||
 | 
			
		||||
use strict;
 | 
			
		||||
 | 
			
		||||
my $cmdline = "mksyscall.pl " . join(' ', @ARGV);
 | 
			
		||||
my $errors = 0;
 | 
			
		||||
my $_32bit = "";
 | 
			
		||||
my $plan9 = 0;
 | 
			
		||||
my $openbsd = 0;
 | 
			
		||||
my $netbsd = 0;
 | 
			
		||||
my $dragonfly = 0;
 | 
			
		||||
my $arm = 0; # 64-bit value should use (even, odd)-pair
 | 
			
		||||
my $tags = "";  # build tags
 | 
			
		||||
 | 
			
		||||
if($ARGV[0] eq "-b32") {
 | 
			
		||||
	$_32bit = "big-endian";
 | 
			
		||||
	shift;
 | 
			
		||||
} elsif($ARGV[0] eq "-l32") {
 | 
			
		||||
	$_32bit = "little-endian";
 | 
			
		||||
	shift;
 | 
			
		||||
}
 | 
			
		||||
if($ARGV[0] eq "-plan9") {
 | 
			
		||||
	$plan9 = 1;
 | 
			
		||||
	shift;
 | 
			
		||||
}
 | 
			
		||||
if($ARGV[0] eq "-openbsd") {
 | 
			
		||||
	$openbsd = 1;
 | 
			
		||||
	shift;
 | 
			
		||||
}
 | 
			
		||||
if($ARGV[0] eq "-netbsd") {
 | 
			
		||||
	$netbsd = 1;
 | 
			
		||||
	shift;
 | 
			
		||||
}
 | 
			
		||||
if($ARGV[0] eq "-dragonfly") {
 | 
			
		||||
	$dragonfly = 1;
 | 
			
		||||
	shift;
 | 
			
		||||
}
 | 
			
		||||
if($ARGV[0] eq "-arm") {
 | 
			
		||||
	$arm = 1;
 | 
			
		||||
	shift;
 | 
			
		||||
}
 | 
			
		||||
if($ARGV[0] eq "-tags") {
 | 
			
		||||
	shift;
 | 
			
		||||
	$tags = $ARGV[0];
 | 
			
		||||
	shift;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if($ARGV[0] =~ /^-/) {
 | 
			
		||||
	print STDERR "usage: mksyscall.pl [-b32 | -l32] [-tags x,y] [file ...]\n";
 | 
			
		||||
	exit 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Check that we are using the new build system if we should
 | 
			
		||||
if($ENV{'GOOS'} eq "linux" && $ENV{'GOARCH'} ne "sparc64") {
 | 
			
		||||
	if($ENV{'GOLANG_SYS_BUILD'} ne "docker") {
 | 
			
		||||
		print STDERR "In the new build system, mksyscall should not be called directly.\n";
 | 
			
		||||
		print STDERR "See README.md\n";
 | 
			
		||||
		exit 1;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
sub parseparamlist($) {
 | 
			
		||||
	my ($list) = @_;
 | 
			
		||||
	$list =~ s/^\s*//;
 | 
			
		||||
	$list =~ s/\s*$//;
 | 
			
		||||
	if($list eq "") {
 | 
			
		||||
		return ();
 | 
			
		||||
	}
 | 
			
		||||
	return split(/\s*,\s*/, $list);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub parseparam($) {
 | 
			
		||||
	my ($p) = @_;
 | 
			
		||||
	if($p !~ /^(\S*) (\S*)$/) {
 | 
			
		||||
		print STDERR "$ARGV:$.: malformed parameter: $p\n";
 | 
			
		||||
		$errors = 1;
 | 
			
		||||
		return ("xx", "int");
 | 
			
		||||
	}
 | 
			
		||||
	return ($1, $2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
my $text = "";
 | 
			
		||||
while(<>) {
 | 
			
		||||
	chomp;
 | 
			
		||||
	s/\s+/ /g;
 | 
			
		||||
	s/^\s+//;
 | 
			
		||||
	s/\s+$//;
 | 
			
		||||
	my $nonblock = /^\/\/sysnb /;
 | 
			
		||||
	next if !/^\/\/sys / && !$nonblock;
 | 
			
		||||
 | 
			
		||||
	# Line must be of the form
 | 
			
		||||
	#	func Open(path string, mode int, perm int) (fd int, errno error)
 | 
			
		||||
	# Split into name, in params, out params.
 | 
			
		||||
	if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*((?i)SYS_[A-Z0-9_]+))?$/) {
 | 
			
		||||
		print STDERR "$ARGV:$.: malformed //sys declaration\n";
 | 
			
		||||
		$errors = 1;
 | 
			
		||||
		next;
 | 
			
		||||
	}
 | 
			
		||||
	my ($func, $in, $out, $sysname) = ($2, $3, $4, $5);
 | 
			
		||||
 | 
			
		||||
	# Split argument lists on comma.
 | 
			
		||||
	my @in = parseparamlist($in);
 | 
			
		||||
	my @out = parseparamlist($out);
 | 
			
		||||
 | 
			
		||||
	# Try in vain to keep people from editing this file.
 | 
			
		||||
	# The theory is that they jump into the middle of the file
 | 
			
		||||
	# without reading the header.
 | 
			
		||||
	$text .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n";
 | 
			
		||||
 | 
			
		||||
	# Go function header.
 | 
			
		||||
	my $out_decl = @out ? sprintf(" (%s)", join(', ', @out)) : "";
 | 
			
		||||
	$text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out_decl;
 | 
			
		||||
 | 
			
		||||
	# Check if err return available
 | 
			
		||||
	my $errvar = "";
 | 
			
		||||
	foreach my $p (@out) {
 | 
			
		||||
		my ($name, $type) = parseparam($p);
 | 
			
		||||
		if($type eq "error") {
 | 
			
		||||
			$errvar = $name;
 | 
			
		||||
			last;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	# Prepare arguments to Syscall.
 | 
			
		||||
	my @args = ();
 | 
			
		||||
	my $n = 0;
 | 
			
		||||
	foreach my $p (@in) {
 | 
			
		||||
		my ($name, $type) = parseparam($p);
 | 
			
		||||
		if($type =~ /^\*/) {
 | 
			
		||||
			push @args, "uintptr(unsafe.Pointer($name))";
 | 
			
		||||
		} elsif($type eq "string" && $errvar ne "") {
 | 
			
		||||
			$text .= "\tvar _p$n *byte\n";
 | 
			
		||||
			$text .= "\t_p$n, $errvar = BytePtrFromString($name)\n";
 | 
			
		||||
			$text .= "\tif $errvar != nil {\n\t\treturn\n\t}\n";
 | 
			
		||||
			push @args, "uintptr(unsafe.Pointer(_p$n))";
 | 
			
		||||
			$n++;
 | 
			
		||||
		} elsif($type eq "string") {
 | 
			
		||||
			print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n";
 | 
			
		||||
			$text .= "\tvar _p$n *byte\n";
 | 
			
		||||
			$text .= "\t_p$n, _ = BytePtrFromString($name)\n";
 | 
			
		||||
			push @args, "uintptr(unsafe.Pointer(_p$n))";
 | 
			
		||||
			$n++;
 | 
			
		||||
		} elsif($type =~ /^\[\](.*)/) {
 | 
			
		||||
			# Convert slice into pointer, length.
 | 
			
		||||
			# Have to be careful not to take address of &a[0] if len == 0:
 | 
			
		||||
			# pass dummy pointer in that case.
 | 
			
		||||
			# Used to pass nil, but some OSes or simulators reject write(fd, nil, 0).
 | 
			
		||||
			$text .= "\tvar _p$n unsafe.Pointer\n";
 | 
			
		||||
			$text .= "\tif len($name) > 0 {\n\t\t_p$n = unsafe.Pointer(\&${name}[0])\n\t}";
 | 
			
		||||
			$text .= " else {\n\t\t_p$n = unsafe.Pointer(&_zero)\n\t}";
 | 
			
		||||
			$text .= "\n";
 | 
			
		||||
			push @args, "uintptr(_p$n)", "uintptr(len($name))";
 | 
			
		||||
			$n++;
 | 
			
		||||
		} elsif($type eq "int64" && ($openbsd || $netbsd)) {
 | 
			
		||||
			push @args, "0";
 | 
			
		||||
			if($_32bit eq "big-endian") {
 | 
			
		||||
				push @args, "uintptr($name>>32)", "uintptr($name)";
 | 
			
		||||
			} elsif($_32bit eq "little-endian") {
 | 
			
		||||
				push @args, "uintptr($name)", "uintptr($name>>32)";
 | 
			
		||||
			} else {
 | 
			
		||||
				push @args, "uintptr($name)";
 | 
			
		||||
			}
 | 
			
		||||
		} elsif($type eq "int64" && $dragonfly) {
 | 
			
		||||
			if ($func !~ /^extp(read|write)/i) {
 | 
			
		||||
				push @args, "0";
 | 
			
		||||
			}
 | 
			
		||||
			if($_32bit eq "big-endian") {
 | 
			
		||||
				push @args, "uintptr($name>>32)", "uintptr($name)";
 | 
			
		||||
			} elsif($_32bit eq "little-endian") {
 | 
			
		||||
				push @args, "uintptr($name)", "uintptr($name>>32)";
 | 
			
		||||
			} else {
 | 
			
		||||
				push @args, "uintptr($name)";
 | 
			
		||||
			}
 | 
			
		||||
		} elsif($type eq "int64" && $_32bit ne "") {
 | 
			
		||||
			if(@args % 2 && $arm) {
 | 
			
		||||
				# arm abi specifies 64-bit argument uses
 | 
			
		||||
				# (even, odd) pair
 | 
			
		||||
				push @args, "0"
 | 
			
		||||
			}
 | 
			
		||||
			if($_32bit eq "big-endian") {
 | 
			
		||||
				push @args, "uintptr($name>>32)", "uintptr($name)";
 | 
			
		||||
			} else {
 | 
			
		||||
				push @args, "uintptr($name)", "uintptr($name>>32)";
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			push @args, "uintptr($name)";
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	# Determine which form to use; pad args with zeros.
 | 
			
		||||
	my $asm = "Syscall";
 | 
			
		||||
	if ($nonblock) {
 | 
			
		||||
		$asm = "RawSyscall";
 | 
			
		||||
	}
 | 
			
		||||
	if(@args <= 3) {
 | 
			
		||||
		while(@args < 3) {
 | 
			
		||||
			push @args, "0";
 | 
			
		||||
		}
 | 
			
		||||
	} elsif(@args <= 6) {
 | 
			
		||||
		$asm .= "6";
 | 
			
		||||
		while(@args < 6) {
 | 
			
		||||
			push @args, "0";
 | 
			
		||||
		}
 | 
			
		||||
	} elsif(@args <= 9) {
 | 
			
		||||
		$asm .= "9";
 | 
			
		||||
		while(@args < 9) {
 | 
			
		||||
			push @args, "0";
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		print STDERR "$ARGV:$.: too many arguments to system call\n";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	# System call number.
 | 
			
		||||
	if($sysname eq "") {
 | 
			
		||||
		$sysname = "SYS_$func";
 | 
			
		||||
		$sysname =~ s/([a-z])([A-Z])/${1}_$2/g;	# turn FooBar into Foo_Bar
 | 
			
		||||
		$sysname =~ y/a-z/A-Z/;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	# Actual call.
 | 
			
		||||
	my $args = join(', ', @args);
 | 
			
		||||
	my $call = "$asm($sysname, $args)";
 | 
			
		||||
 | 
			
		||||
	# Assign return values.
 | 
			
		||||
	my $body = "";
 | 
			
		||||
	my @ret = ("_", "_", "_");
 | 
			
		||||
	my $do_errno = 0;
 | 
			
		||||
	for(my $i=0; $i<@out; $i++) {
 | 
			
		||||
		my $p = $out[$i];
 | 
			
		||||
		my ($name, $type) = parseparam($p);
 | 
			
		||||
		my $reg = "";
 | 
			
		||||
		if($name eq "err" && !$plan9) {
 | 
			
		||||
			$reg = "e1";
 | 
			
		||||
			$ret[2] = $reg;
 | 
			
		||||
			$do_errno = 1;
 | 
			
		||||
		} elsif($name eq "err" && $plan9) {
 | 
			
		||||
			$ret[0] = "r0";
 | 
			
		||||
			$ret[2] = "e1";
 | 
			
		||||
			next;
 | 
			
		||||
		} else {
 | 
			
		||||
			$reg = sprintf("r%d", $i);
 | 
			
		||||
			$ret[$i] = $reg;
 | 
			
		||||
		}
 | 
			
		||||
		if($type eq "bool") {
 | 
			
		||||
			$reg = "$reg != 0";
 | 
			
		||||
		}
 | 
			
		||||
		if($type eq "int64" && $_32bit ne "") {
 | 
			
		||||
			# 64-bit number in r1:r0 or r0:r1.
 | 
			
		||||
			if($i+2 > @out) {
 | 
			
		||||
				print STDERR "$ARGV:$.: not enough registers for int64 return\n";
 | 
			
		||||
			}
 | 
			
		||||
			if($_32bit eq "big-endian") {
 | 
			
		||||
				$reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i, $i+1);
 | 
			
		||||
			} else {
 | 
			
		||||
				$reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i+1, $i);
 | 
			
		||||
			}
 | 
			
		||||
			$ret[$i] = sprintf("r%d", $i);
 | 
			
		||||
			$ret[$i+1] = sprintf("r%d", $i+1);
 | 
			
		||||
		}
 | 
			
		||||
		if($reg ne "e1" || $plan9) {
 | 
			
		||||
			$body .= "\t$name = $type($reg)\n";
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if ($ret[0] eq "_" && $ret[1] eq "_" && $ret[2] eq "_") {
 | 
			
		||||
		$text .= "\t$call\n";
 | 
			
		||||
	} else {
 | 
			
		||||
		$text .= "\t$ret[0], $ret[1], $ret[2] := $call\n";
 | 
			
		||||
	}
 | 
			
		||||
	$text .= $body;
 | 
			
		||||
 | 
			
		||||
	if ($plan9 && $ret[2] eq "e1") {
 | 
			
		||||
		$text .= "\tif int32(r0) == -1 {\n";
 | 
			
		||||
		$text .= "\t\terr = e1\n";
 | 
			
		||||
		$text .= "\t}\n";
 | 
			
		||||
	} elsif ($do_errno) {
 | 
			
		||||
		$text .= "\tif e1 != 0 {\n";
 | 
			
		||||
		$text .= "\t\terr = errnoErr(e1)\n";
 | 
			
		||||
		$text .= "\t}\n";
 | 
			
		||||
	}
 | 
			
		||||
	$text .= "\treturn\n";
 | 
			
		||||
	$text .= "}\n\n";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
chomp $text;
 | 
			
		||||
chomp $text;
 | 
			
		||||
 | 
			
		||||
if($errors) {
 | 
			
		||||
	exit 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
print <<EOF;
 | 
			
		||||
// $cmdline
 | 
			
		||||
// Code generated by the command above; see README.md. DO NOT EDIT.
 | 
			
		||||
 | 
			
		||||
// +build $tags
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var _ syscall.Errno
 | 
			
		||||
 | 
			
		||||
$text
 | 
			
		||||
EOF
 | 
			
		||||
exit 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,289 @@
 | 
			
		|||
#!/usr/bin/env perl
 | 
			
		||||
# Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
# Use of this source code is governed by a BSD-style
 | 
			
		||||
# license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
# This program reads a file containing function prototypes
 | 
			
		||||
# (like syscall_solaris.go) and generates system call bodies.
 | 
			
		||||
# The prototypes are marked by lines beginning with "//sys"
 | 
			
		||||
# and read like func declarations if //sys is replaced by func, but:
 | 
			
		||||
#	* The parameter lists must give a name for each argument.
 | 
			
		||||
#	  This includes return parameters.
 | 
			
		||||
#	* The parameter lists must give a type for each argument:
 | 
			
		||||
#	  the (x, y, z int) shorthand is not allowed.
 | 
			
		||||
#	* If the return parameter is an error number, it must be named err.
 | 
			
		||||
#	* If go func name needs to be different than its libc name,
 | 
			
		||||
#	* or the function is not in libc, name could be specified
 | 
			
		||||
#	* at the end, after "=" sign, like
 | 
			
		||||
#	  //sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) = libsocket.getsockopt
 | 
			
		||||
 | 
			
		||||
use strict;
 | 
			
		||||
 | 
			
		||||
my $cmdline = "mksyscall_solaris.pl " . join(' ', @ARGV);
 | 
			
		||||
my $errors = 0;
 | 
			
		||||
my $_32bit = "";
 | 
			
		||||
my $tags = "";  # build tags
 | 
			
		||||
 | 
			
		||||
binmode STDOUT;
 | 
			
		||||
 | 
			
		||||
if($ARGV[0] eq "-b32") {
 | 
			
		||||
	$_32bit = "big-endian";
 | 
			
		||||
	shift;
 | 
			
		||||
} elsif($ARGV[0] eq "-l32") {
 | 
			
		||||
	$_32bit = "little-endian";
 | 
			
		||||
	shift;
 | 
			
		||||
}
 | 
			
		||||
if($ARGV[0] eq "-tags") {
 | 
			
		||||
	shift;
 | 
			
		||||
	$tags = $ARGV[0];
 | 
			
		||||
	shift;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if($ARGV[0] =~ /^-/) {
 | 
			
		||||
	print STDERR "usage: mksyscall_solaris.pl [-b32 | -l32] [-tags x,y] [file ...]\n";
 | 
			
		||||
	exit 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub parseparamlist($) {
 | 
			
		||||
	my ($list) = @_;
 | 
			
		||||
	$list =~ s/^\s*//;
 | 
			
		||||
	$list =~ s/\s*$//;
 | 
			
		||||
	if($list eq "") {
 | 
			
		||||
		return ();
 | 
			
		||||
	}
 | 
			
		||||
	return split(/\s*,\s*/, $list);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub parseparam($) {
 | 
			
		||||
	my ($p) = @_;
 | 
			
		||||
	if($p !~ /^(\S*) (\S*)$/) {
 | 
			
		||||
		print STDERR "$ARGV:$.: malformed parameter: $p\n";
 | 
			
		||||
		$errors = 1;
 | 
			
		||||
		return ("xx", "int");
 | 
			
		||||
	}
 | 
			
		||||
	return ($1, $2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
my $package = "";
 | 
			
		||||
my $text = "";
 | 
			
		||||
my $dynimports = "";
 | 
			
		||||
my $linknames = "";
 | 
			
		||||
my @vars = ();
 | 
			
		||||
while(<>) {
 | 
			
		||||
	chomp;
 | 
			
		||||
	s/\s+/ /g;
 | 
			
		||||
	s/^\s+//;
 | 
			
		||||
	s/\s+$//;
 | 
			
		||||
	$package = $1 if !$package && /^package (\S+)$/;
 | 
			
		||||
	my $nonblock = /^\/\/sysnb /;
 | 
			
		||||
	next if !/^\/\/sys / && !$nonblock;
 | 
			
		||||
 | 
			
		||||
	# Line must be of the form
 | 
			
		||||
	#	func Open(path string, mode int, perm int) (fd int, err error)
 | 
			
		||||
	# Split into name, in params, out params.
 | 
			
		||||
	if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$/) {
 | 
			
		||||
		print STDERR "$ARGV:$.: malformed //sys declaration\n";
 | 
			
		||||
		$errors = 1;
 | 
			
		||||
		next;
 | 
			
		||||
	}
 | 
			
		||||
	my ($nb, $func, $in, $out, $modname, $sysname) = ($1, $2, $3, $4, $5, $6);
 | 
			
		||||
 | 
			
		||||
	# Split argument lists on comma.
 | 
			
		||||
	my @in = parseparamlist($in);
 | 
			
		||||
	my @out = parseparamlist($out);
 | 
			
		||||
 | 
			
		||||
	# So file name.
 | 
			
		||||
	if($modname eq "") {
 | 
			
		||||
		$modname = "libc";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	# System call name.
 | 
			
		||||
	if($sysname eq "") {
 | 
			
		||||
		$sysname = "$func";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	# System call pointer variable name.
 | 
			
		||||
	my $sysvarname = "proc$sysname";
 | 
			
		||||
 | 
			
		||||
	my $strconvfunc = "BytePtrFromString";
 | 
			
		||||
	my $strconvtype = "*byte";
 | 
			
		||||
 | 
			
		||||
	$sysname =~ y/A-Z/a-z/; # All libc functions are lowercase.
 | 
			
		||||
 | 
			
		||||
	# Runtime import of function to allow cross-platform builds.
 | 
			
		||||
	$dynimports .= "//go:cgo_import_dynamic libc_${sysname} ${sysname} \"$modname.so\"\n";
 | 
			
		||||
	# Link symbol to proc address variable.
 | 
			
		||||
	$linknames .= "//go:linkname ${sysvarname} libc_${sysname}\n";
 | 
			
		||||
	# Library proc address variable.
 | 
			
		||||
	push @vars, $sysvarname;
 | 
			
		||||
 | 
			
		||||
	# Go function header.
 | 
			
		||||
	$out = join(', ', @out);
 | 
			
		||||
	if($out ne "") {
 | 
			
		||||
		$out = " ($out)";
 | 
			
		||||
	}
 | 
			
		||||
	if($text ne "") {
 | 
			
		||||
		$text .= "\n"
 | 
			
		||||
	}
 | 
			
		||||
	$text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out;
 | 
			
		||||
 | 
			
		||||
	# Check if err return available
 | 
			
		||||
	my $errvar = "";
 | 
			
		||||
	foreach my $p (@out) {
 | 
			
		||||
		my ($name, $type) = parseparam($p);
 | 
			
		||||
		if($type eq "error") {
 | 
			
		||||
			$errvar = $name;
 | 
			
		||||
			last;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	# Prepare arguments to Syscall.
 | 
			
		||||
	my @args = ();
 | 
			
		||||
	my $n = 0;
 | 
			
		||||
	foreach my $p (@in) {
 | 
			
		||||
		my ($name, $type) = parseparam($p);
 | 
			
		||||
		if($type =~ /^\*/) {
 | 
			
		||||
			push @args, "uintptr(unsafe.Pointer($name))";
 | 
			
		||||
		} elsif($type eq "string" && $errvar ne "") {
 | 
			
		||||
			$text .= "\tvar _p$n $strconvtype\n";
 | 
			
		||||
			$text .= "\t_p$n, $errvar = $strconvfunc($name)\n";
 | 
			
		||||
			$text .= "\tif $errvar != nil {\n\t\treturn\n\t}\n";
 | 
			
		||||
			push @args, "uintptr(unsafe.Pointer(_p$n))";
 | 
			
		||||
			$n++;
 | 
			
		||||
		} elsif($type eq "string") {
 | 
			
		||||
			print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n";
 | 
			
		||||
			$text .= "\tvar _p$n $strconvtype\n";
 | 
			
		||||
			$text .= "\t_p$n, _ = $strconvfunc($name)\n";
 | 
			
		||||
			push @args, "uintptr(unsafe.Pointer(_p$n))";
 | 
			
		||||
			$n++;
 | 
			
		||||
		} elsif($type =~ /^\[\](.*)/) {
 | 
			
		||||
			# Convert slice into pointer, length.
 | 
			
		||||
			# Have to be careful not to take address of &a[0] if len == 0:
 | 
			
		||||
			# pass nil in that case.
 | 
			
		||||
			$text .= "\tvar _p$n *$1\n";
 | 
			
		||||
			$text .= "\tif len($name) > 0 {\n\t\t_p$n = \&$name\[0]\n\t}\n";
 | 
			
		||||
			push @args, "uintptr(unsafe.Pointer(_p$n))", "uintptr(len($name))";
 | 
			
		||||
			$n++;
 | 
			
		||||
		} elsif($type eq "int64" && $_32bit ne "") {
 | 
			
		||||
			if($_32bit eq "big-endian") {
 | 
			
		||||
				push @args, "uintptr($name >> 32)", "uintptr($name)";
 | 
			
		||||
			} else {
 | 
			
		||||
				push @args, "uintptr($name)", "uintptr($name >> 32)";
 | 
			
		||||
			}
 | 
			
		||||
		} elsif($type eq "bool") {
 | 
			
		||||
 			$text .= "\tvar _p$n uint32\n";
 | 
			
		||||
			$text .= "\tif $name {\n\t\t_p$n = 1\n\t} else {\n\t\t_p$n = 0\n\t}\n";
 | 
			
		||||
			push @args, "uintptr(_p$n)";
 | 
			
		||||
			$n++;
 | 
			
		||||
		} else {
 | 
			
		||||
			push @args, "uintptr($name)";
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	my $nargs = @args;
 | 
			
		||||
 | 
			
		||||
	# Determine which form to use; pad args with zeros.
 | 
			
		||||
	my $asm = "sysvicall6";
 | 
			
		||||
	if ($nonblock) {
 | 
			
		||||
		$asm = "rawSysvicall6";
 | 
			
		||||
	}
 | 
			
		||||
	if(@args <= 6) {
 | 
			
		||||
		while(@args < 6) {
 | 
			
		||||
			push @args, "0";
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		print STDERR "$ARGV:$.: too many arguments to system call\n";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	# Actual call.
 | 
			
		||||
	my $args = join(', ', @args);
 | 
			
		||||
	my $call = "$asm(uintptr(unsafe.Pointer(&$sysvarname)), $nargs, $args)";
 | 
			
		||||
 | 
			
		||||
	# Assign return values.
 | 
			
		||||
	my $body = "";
 | 
			
		||||
	my $failexpr = "";
 | 
			
		||||
	my @ret = ("_", "_", "_");
 | 
			
		||||
	my @pout= ();
 | 
			
		||||
	my $do_errno = 0;
 | 
			
		||||
	for(my $i=0; $i<@out; $i++) {
 | 
			
		||||
		my $p = $out[$i];
 | 
			
		||||
		my ($name, $type) = parseparam($p);
 | 
			
		||||
		my $reg = "";
 | 
			
		||||
		if($name eq "err") {
 | 
			
		||||
			$reg = "e1";
 | 
			
		||||
			$ret[2] = $reg;
 | 
			
		||||
			$do_errno = 1;
 | 
			
		||||
		} else {
 | 
			
		||||
			$reg = sprintf("r%d", $i);
 | 
			
		||||
			$ret[$i] = $reg;
 | 
			
		||||
		}
 | 
			
		||||
		if($type eq "bool") {
 | 
			
		||||
			$reg = "$reg != 0";
 | 
			
		||||
		}
 | 
			
		||||
		if($type eq "int64" && $_32bit ne "") {
 | 
			
		||||
			# 64-bit number in r1:r0 or r0:r1.
 | 
			
		||||
			if($i+2 > @out) {
 | 
			
		||||
				print STDERR "$ARGV:$.: not enough registers for int64 return\n";
 | 
			
		||||
			}
 | 
			
		||||
			if($_32bit eq "big-endian") {
 | 
			
		||||
				$reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i, $i+1);
 | 
			
		||||
			} else {
 | 
			
		||||
				$reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i+1, $i);
 | 
			
		||||
			}
 | 
			
		||||
			$ret[$i] = sprintf("r%d", $i);
 | 
			
		||||
			$ret[$i+1] = sprintf("r%d", $i+1);
 | 
			
		||||
		}
 | 
			
		||||
		if($reg ne "e1") {
 | 
			
		||||
			$body .= "\t$name = $type($reg)\n";
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if ($ret[0] eq "_" && $ret[1] eq "_" && $ret[2] eq "_") {
 | 
			
		||||
		$text .= "\t$call\n";
 | 
			
		||||
	} else {
 | 
			
		||||
		$text .= "\t$ret[0], $ret[1], $ret[2] := $call\n";
 | 
			
		||||
	}
 | 
			
		||||
	$text .= $body;
 | 
			
		||||
 | 
			
		||||
	if ($do_errno) {
 | 
			
		||||
		$text .= "\tif e1 != 0 {\n";
 | 
			
		||||
		$text .= "\t\terr = e1\n";
 | 
			
		||||
		$text .= "\t}\n";
 | 
			
		||||
	}
 | 
			
		||||
	$text .= "\treturn\n";
 | 
			
		||||
	$text .= "}\n";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if($errors) {
 | 
			
		||||
	exit 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
print <<EOF;
 | 
			
		||||
// $cmdline
 | 
			
		||||
// Code generated by the command above; see README.md. DO NOT EDIT.
 | 
			
		||||
 | 
			
		||||
// +build $tags
 | 
			
		||||
 | 
			
		||||
package $package
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
)
 | 
			
		||||
EOF
 | 
			
		||||
 | 
			
		||||
print "import \"golang.org/x/sys/unix\"\n" if $package ne "unix";
 | 
			
		||||
 | 
			
		||||
my $vardecls = "\t" . join(",\n\t", @vars);
 | 
			
		||||
$vardecls .= " syscallFunc";
 | 
			
		||||
 | 
			
		||||
chomp($_=<<EOF);
 | 
			
		||||
 | 
			
		||||
$dynimports
 | 
			
		||||
$linknames
 | 
			
		||||
var (
 | 
			
		||||
$vardecls
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
$text
 | 
			
		||||
EOF
 | 
			
		||||
print $_;
 | 
			
		||||
exit 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,264 @@
 | 
			
		|||
#!/usr/bin/env perl
 | 
			
		||||
 | 
			
		||||
# Copyright 2011 The Go Authors. All rights reserved.
 | 
			
		||||
# Use of this source code is governed by a BSD-style
 | 
			
		||||
# license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Parse the header files for OpenBSD and generate a Go usable sysctl MIB.
 | 
			
		||||
#
 | 
			
		||||
# Build a MIB with each entry being an array containing the level, type and
 | 
			
		||||
# a hash that will contain additional entries if the current entry is a node.
 | 
			
		||||
# We then walk this MIB and create a flattened sysctl name to OID hash.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
use strict;
 | 
			
		||||
 | 
			
		||||
if($ENV{'GOARCH'} eq "" || $ENV{'GOOS'} eq "") {
 | 
			
		||||
	print STDERR "GOARCH or GOOS not defined in environment\n";
 | 
			
		||||
	exit 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
my $debug = 0;
 | 
			
		||||
my %ctls = ();
 | 
			
		||||
 | 
			
		||||
my @headers = qw (
 | 
			
		||||
	sys/sysctl.h
 | 
			
		||||
	sys/socket.h
 | 
			
		||||
	sys/tty.h
 | 
			
		||||
	sys/malloc.h
 | 
			
		||||
	sys/mount.h
 | 
			
		||||
	sys/namei.h
 | 
			
		||||
	sys/sem.h
 | 
			
		||||
	sys/shm.h
 | 
			
		||||
	sys/vmmeter.h
 | 
			
		||||
	uvm/uvm_param.h
 | 
			
		||||
	uvm/uvm_swap_encrypt.h
 | 
			
		||||
	ddb/db_var.h
 | 
			
		||||
	net/if.h
 | 
			
		||||
	net/if_pfsync.h
 | 
			
		||||
	net/pipex.h
 | 
			
		||||
	netinet/in.h
 | 
			
		||||
	netinet/icmp_var.h
 | 
			
		||||
	netinet/igmp_var.h
 | 
			
		||||
	netinet/ip_ah.h
 | 
			
		||||
	netinet/ip_carp.h
 | 
			
		||||
	netinet/ip_divert.h
 | 
			
		||||
	netinet/ip_esp.h
 | 
			
		||||
	netinet/ip_ether.h
 | 
			
		||||
	netinet/ip_gre.h
 | 
			
		||||
	netinet/ip_ipcomp.h
 | 
			
		||||
	netinet/ip_ipip.h
 | 
			
		||||
	netinet/pim_var.h
 | 
			
		||||
	netinet/tcp_var.h
 | 
			
		||||
	netinet/udp_var.h
 | 
			
		||||
	netinet6/in6.h
 | 
			
		||||
	netinet6/ip6_divert.h
 | 
			
		||||
	netinet6/pim6_var.h
 | 
			
		||||
	netinet/icmp6.h
 | 
			
		||||
	netmpls/mpls.h
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
my @ctls = qw (
 | 
			
		||||
	kern
 | 
			
		||||
	vm
 | 
			
		||||
	fs
 | 
			
		||||
	net
 | 
			
		||||
	#debug				# Special handling required
 | 
			
		||||
	hw
 | 
			
		||||
	#machdep			# Arch specific
 | 
			
		||||
	user
 | 
			
		||||
	ddb
 | 
			
		||||
	#vfs				# Special handling required
 | 
			
		||||
	fs.posix
 | 
			
		||||
	kern.forkstat
 | 
			
		||||
	kern.intrcnt
 | 
			
		||||
	kern.malloc
 | 
			
		||||
	kern.nchstats
 | 
			
		||||
	kern.seminfo
 | 
			
		||||
	kern.shminfo
 | 
			
		||||
	kern.timecounter
 | 
			
		||||
	kern.tty
 | 
			
		||||
	kern.watchdog
 | 
			
		||||
	net.bpf
 | 
			
		||||
	net.ifq
 | 
			
		||||
	net.inet
 | 
			
		||||
	net.inet.ah
 | 
			
		||||
	net.inet.carp
 | 
			
		||||
	net.inet.divert
 | 
			
		||||
	net.inet.esp
 | 
			
		||||
	net.inet.etherip
 | 
			
		||||
	net.inet.gre
 | 
			
		||||
	net.inet.icmp
 | 
			
		||||
	net.inet.igmp
 | 
			
		||||
	net.inet.ip
 | 
			
		||||
	net.inet.ip.ifq
 | 
			
		||||
	net.inet.ipcomp
 | 
			
		||||
	net.inet.ipip
 | 
			
		||||
	net.inet.mobileip
 | 
			
		||||
	net.inet.pfsync
 | 
			
		||||
	net.inet.pim
 | 
			
		||||
	net.inet.tcp
 | 
			
		||||
	net.inet.udp
 | 
			
		||||
	net.inet6
 | 
			
		||||
	net.inet6.divert
 | 
			
		||||
	net.inet6.ip6
 | 
			
		||||
	net.inet6.icmp6
 | 
			
		||||
	net.inet6.pim6
 | 
			
		||||
	net.inet6.tcp6
 | 
			
		||||
	net.inet6.udp6
 | 
			
		||||
	net.mpls
 | 
			
		||||
	net.mpls.ifq
 | 
			
		||||
	net.key
 | 
			
		||||
	net.pflow
 | 
			
		||||
	net.pfsync
 | 
			
		||||
	net.pipex
 | 
			
		||||
	net.rt
 | 
			
		||||
	vm.swapencrypt
 | 
			
		||||
	#vfsgenctl			# Special handling required
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
# Node name "fixups"
 | 
			
		||||
my %ctl_map = (
 | 
			
		||||
	"ipproto" => "net.inet",
 | 
			
		||||
	"net.inet.ipproto" => "net.inet",
 | 
			
		||||
	"net.inet6.ipv6proto" => "net.inet6",
 | 
			
		||||
	"net.inet6.ipv6" => "net.inet6.ip6",
 | 
			
		||||
	"net.inet.icmpv6" => "net.inet6.icmp6",
 | 
			
		||||
	"net.inet6.divert6" => "net.inet6.divert",
 | 
			
		||||
	"net.inet6.tcp6" => "net.inet.tcp",
 | 
			
		||||
	"net.inet6.udp6" => "net.inet.udp",
 | 
			
		||||
	"mpls" => "net.mpls",
 | 
			
		||||
	"swpenc" => "vm.swapencrypt"
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
# Node mappings
 | 
			
		||||
my %node_map = (
 | 
			
		||||
	"net.inet.ip.ifq" => "net.ifq",
 | 
			
		||||
	"net.inet.pfsync" => "net.pfsync",
 | 
			
		||||
	"net.mpls.ifq" => "net.ifq"
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
my $ctlname;
 | 
			
		||||
my %mib = ();
 | 
			
		||||
my %sysctl = ();
 | 
			
		||||
my $node;
 | 
			
		||||
 | 
			
		||||
sub debug() {
 | 
			
		||||
	print STDERR "$_[0]\n" if $debug;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Walk the MIB and build a sysctl name to OID mapping.
 | 
			
		||||
sub build_sysctl() {
 | 
			
		||||
	my ($node, $name, $oid) = @_;
 | 
			
		||||
	my %node = %{$node};
 | 
			
		||||
	my @oid = @{$oid};
 | 
			
		||||
 | 
			
		||||
	foreach my $key (sort keys %node) {
 | 
			
		||||
		my @node = @{$node{$key}};
 | 
			
		||||
		my $nodename = $name.($name ne '' ? '.' : '').$key;
 | 
			
		||||
		my @nodeoid = (@oid, $node[0]);
 | 
			
		||||
		if ($node[1] eq 'CTLTYPE_NODE') {
 | 
			
		||||
			if (exists $node_map{$nodename}) {
 | 
			
		||||
				$node = \%mib;
 | 
			
		||||
				$ctlname = $node_map{$nodename};
 | 
			
		||||
				foreach my $part (split /\./, $ctlname) {
 | 
			
		||||
					$node = \%{@{$$node{$part}}[2]};
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				$node = $node[2];
 | 
			
		||||
			}
 | 
			
		||||
			&build_sysctl($node, $nodename, \@nodeoid);
 | 
			
		||||
		} elsif ($node[1] ne '') {
 | 
			
		||||
			$sysctl{$nodename} = \@nodeoid;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
foreach my $ctl (@ctls) {
 | 
			
		||||
	$ctls{$ctl} = $ctl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Build MIB
 | 
			
		||||
foreach my $header (@headers) {
 | 
			
		||||
	&debug("Processing $header...");
 | 
			
		||||
	open HEADER, "/usr/include/$header" ||
 | 
			
		||||
	    print STDERR "Failed to open $header\n";
 | 
			
		||||
	while (<HEADER>) {
 | 
			
		||||
		if ($_ =~ /^#define\s+(CTL_NAMES)\s+{/ ||
 | 
			
		||||
		    $_ =~ /^#define\s+(CTL_(.*)_NAMES)\s+{/ ||
 | 
			
		||||
		    $_ =~ /^#define\s+((.*)CTL_NAMES)\s+{/) {
 | 
			
		||||
			if ($1 eq 'CTL_NAMES') {
 | 
			
		||||
				# Top level.
 | 
			
		||||
				$node = \%mib;
 | 
			
		||||
			} else {
 | 
			
		||||
				# Node.
 | 
			
		||||
				my $nodename = lc($2);
 | 
			
		||||
				if ($header =~ /^netinet\//) {
 | 
			
		||||
					$ctlname = "net.inet.$nodename";
 | 
			
		||||
				} elsif ($header =~ /^netinet6\//) {
 | 
			
		||||
					$ctlname = "net.inet6.$nodename";
 | 
			
		||||
				} elsif ($header =~ /^net\//) {
 | 
			
		||||
					$ctlname = "net.$nodename";
 | 
			
		||||
				} else {
 | 
			
		||||
					$ctlname = "$nodename";
 | 
			
		||||
					$ctlname =~ s/^(fs|net|kern)_/$1\./;
 | 
			
		||||
				}
 | 
			
		||||
				if (exists $ctl_map{$ctlname}) {
 | 
			
		||||
					$ctlname = $ctl_map{$ctlname};
 | 
			
		||||
				}
 | 
			
		||||
				if (not exists $ctls{$ctlname}) {
 | 
			
		||||
					&debug("Ignoring $ctlname...");
 | 
			
		||||
					next;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				# Walk down from the top of the MIB.
 | 
			
		||||
				$node = \%mib;
 | 
			
		||||
				foreach my $part (split /\./, $ctlname) {
 | 
			
		||||
					if (not exists $$node{$part}) {
 | 
			
		||||
						&debug("Missing node $part");
 | 
			
		||||
						$$node{$part} = [ 0, '', {} ];
 | 
			
		||||
					}
 | 
			
		||||
					$node = \%{@{$$node{$part}}[2]};
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			# Populate current node with entries.
 | 
			
		||||
			my $i = -1;
 | 
			
		||||
			while (defined($_) && $_ !~ /^}/) {
 | 
			
		||||
				$_ = <HEADER>;
 | 
			
		||||
				$i++ if $_ =~ /{.*}/;
 | 
			
		||||
				next if $_ !~ /{\s+"(\w+)",\s+(CTLTYPE_[A-Z]+)\s+}/;
 | 
			
		||||
				$$node{$1} = [ $i, $2, {} ];
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	close HEADER;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
&build_sysctl(\%mib, "", []);
 | 
			
		||||
 | 
			
		||||
print <<EOF;
 | 
			
		||||
// mksysctl_openbsd.pl
 | 
			
		||||
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 | 
			
		||||
 | 
			
		||||
// +build $ENV{'GOARCH'},$ENV{'GOOS'}
 | 
			
		||||
 | 
			
		||||
package unix;
 | 
			
		||||
 | 
			
		||||
type mibentry struct {
 | 
			
		||||
	ctlname string
 | 
			
		||||
	ctloid []_C_int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var sysctlMib = []mibentry {
 | 
			
		||||
EOF
 | 
			
		||||
 | 
			
		||||
foreach my $name (sort keys %sysctl) {
 | 
			
		||||
	my @oid = @{$sysctl{$name}};
 | 
			
		||||
	print "\t{ \"$name\", []_C_int{ ", join(', ', @oid), " } }, \n";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
print <<EOF;
 | 
			
		||||
}
 | 
			
		||||
EOF
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,39 @@
 | 
			
		|||
#!/usr/bin/env perl
 | 
			
		||||
# Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
# Use of this source code is governed by a BSD-style
 | 
			
		||||
# license that can be found in the LICENSE file.
 | 
			
		||||
#
 | 
			
		||||
# Generate system call table for Darwin from sys/syscall.h
 | 
			
		||||
 | 
			
		||||
use strict;
 | 
			
		||||
 | 
			
		||||
if($ENV{'GOARCH'} eq "" || $ENV{'GOOS'} eq "") {
 | 
			
		||||
	print STDERR "GOARCH or GOOS not defined in environment\n";
 | 
			
		||||
	exit 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
my $command = "mksysnum_darwin.pl " . join(' ', @ARGV);
 | 
			
		||||
 | 
			
		||||
print <<EOF;
 | 
			
		||||
// $command
 | 
			
		||||
// Code generated by the command above; see README.md. DO NOT EDIT.
 | 
			
		||||
 | 
			
		||||
// +build $ENV{'GOARCH'},$ENV{'GOOS'}
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
EOF
 | 
			
		||||
 | 
			
		||||
while(<>){
 | 
			
		||||
	if(/^#define\s+SYS_(\w+)\s+([0-9]+)/){
 | 
			
		||||
		my $name = $1;
 | 
			
		||||
		my $num = $2;
 | 
			
		||||
		$name =~ y/a-z/A-Z/;
 | 
			
		||||
		print "	SYS_$name = $num;"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
print <<EOF;
 | 
			
		||||
)
 | 
			
		||||
EOF
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
#!/usr/bin/env perl
 | 
			
		||||
# Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
# Use of this source code is governed by a BSD-style
 | 
			
		||||
# license that can be found in the LICENSE file.
 | 
			
		||||
#
 | 
			
		||||
# Generate system call table for DragonFly from master list
 | 
			
		||||
# (for example, /usr/src/sys/kern/syscalls.master).
 | 
			
		||||
 | 
			
		||||
use strict;
 | 
			
		||||
 | 
			
		||||
if($ENV{'GOARCH'} eq "" || $ENV{'GOOS'} eq "") {
 | 
			
		||||
	print STDERR "GOARCH or GOOS not defined in environment\n";
 | 
			
		||||
	exit 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
my $command = "mksysnum_dragonfly.pl " . join(' ', @ARGV);
 | 
			
		||||
 | 
			
		||||
print <<EOF;
 | 
			
		||||
// $command
 | 
			
		||||
// Code generated by the command above; see README.md. DO NOT EDIT.
 | 
			
		||||
 | 
			
		||||
// +build $ENV{'GOARCH'},$ENV{'GOOS'}
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
EOF
 | 
			
		||||
 | 
			
		||||
while(<>){
 | 
			
		||||
	if(/^([0-9]+)\s+STD\s+({ \S+\s+(\w+).*)$/){
 | 
			
		||||
		my $num = $1;
 | 
			
		||||
		my $proto = $2;
 | 
			
		||||
		my $name = "SYS_$3";
 | 
			
		||||
		$name =~ y/a-z/A-Z/;
 | 
			
		||||
 | 
			
		||||
		# There are multiple entries for enosys and nosys, so comment them out.
 | 
			
		||||
		if($name =~ /^SYS_E?NOSYS$/){
 | 
			
		||||
			$name = "// $name";
 | 
			
		||||
		}
 | 
			
		||||
		if($name eq 'SYS_SYS_EXIT'){
 | 
			
		||||
			$name = 'SYS_EXIT';
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		print "	$name = $num;  // $proto\n";
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
print <<EOF;
 | 
			
		||||
)
 | 
			
		||||
EOF
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,63 @@
 | 
			
		|||
#!/usr/bin/env perl
 | 
			
		||||
# Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
# Use of this source code is governed by a BSD-style
 | 
			
		||||
# license that can be found in the LICENSE file.
 | 
			
		||||
#
 | 
			
		||||
# Generate system call table for FreeBSD from master list
 | 
			
		||||
# (for example, /usr/src/sys/kern/syscalls.master).
 | 
			
		||||
 | 
			
		||||
use strict;
 | 
			
		||||
 | 
			
		||||
if($ENV{'GOARCH'} eq "" || $ENV{'GOOS'} eq "") {
 | 
			
		||||
	print STDERR "GOARCH or GOOS not defined in environment\n";
 | 
			
		||||
	exit 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
my $command = "mksysnum_freebsd.pl " . join(' ', @ARGV);
 | 
			
		||||
 | 
			
		||||
print <<EOF;
 | 
			
		||||
// $command
 | 
			
		||||
// Code generated by the command above; see README.md. DO NOT EDIT.
 | 
			
		||||
 | 
			
		||||
// +build $ENV{'GOARCH'},$ENV{'GOOS'}
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
EOF
 | 
			
		||||
 | 
			
		||||
while(<>){
 | 
			
		||||
	if(/^([0-9]+)\s+\S+\s+STD\s+({ \S+\s+(\w+).*)$/){
 | 
			
		||||
		my $num = $1;
 | 
			
		||||
		my $proto = $2;
 | 
			
		||||
		my $name = "SYS_$3";
 | 
			
		||||
		$name =~ y/a-z/A-Z/;
 | 
			
		||||
 | 
			
		||||
		# There are multiple entries for enosys and nosys, so comment them out.
 | 
			
		||||
		if($name =~ /^SYS_E?NOSYS$/){
 | 
			
		||||
			$name = "// $name";
 | 
			
		||||
		}
 | 
			
		||||
		if($name eq 'SYS_SYS_EXIT'){
 | 
			
		||||
			$name = 'SYS_EXIT';
 | 
			
		||||
		}
 | 
			
		||||
		if($name =~ /^SYS_CAP_+/ || $name =~ /^SYS___CAP_+/){
 | 
			
		||||
			next
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		print "	$name = $num;  // $proto\n";
 | 
			
		||||
 | 
			
		||||
		# We keep Capsicum syscall numbers for FreeBSD
 | 
			
		||||
		# 9-STABLE here because we are not sure whether they
 | 
			
		||||
		# are mature and stable.
 | 
			
		||||
		if($num == 513){
 | 
			
		||||
			print " SYS_CAP_NEW = 514 // { int cap_new(int fd, uint64_t rights); }\n";
 | 
			
		||||
			print " SYS_CAP_GETRIGHTS = 515 // { int cap_getrights(int fd, \\\n";
 | 
			
		||||
			print " SYS_CAP_ENTER = 516 // { int cap_enter(void); }\n";
 | 
			
		||||
			print " SYS_CAP_GETMODE = 517 // { int cap_getmode(u_int *modep); }\n";
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
print <<EOF;
 | 
			
		||||
)
 | 
			
		||||
EOF
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,58 @@
 | 
			
		|||
#!/usr/bin/env perl
 | 
			
		||||
# Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
# Use of this source code is governed by a BSD-style
 | 
			
		||||
# license that can be found in the LICENSE file.
 | 
			
		||||
#
 | 
			
		||||
# Generate system call table for OpenBSD from master list
 | 
			
		||||
# (for example, /usr/src/sys/kern/syscalls.master).
 | 
			
		||||
 | 
			
		||||
use strict;
 | 
			
		||||
 | 
			
		||||
if($ENV{'GOARCH'} eq "" || $ENV{'GOOS'} eq "") {
 | 
			
		||||
	print STDERR "GOARCH or GOOS not defined in environment\n";
 | 
			
		||||
	exit 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
my $command = "mksysnum_netbsd.pl " . join(' ', @ARGV);
 | 
			
		||||
 | 
			
		||||
print <<EOF;
 | 
			
		||||
// $command
 | 
			
		||||
// Code generated by the command above; see README.md. DO NOT EDIT.
 | 
			
		||||
 | 
			
		||||
// +build $ENV{'GOARCH'},$ENV{'GOOS'}
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
EOF
 | 
			
		||||
 | 
			
		||||
my $line = '';
 | 
			
		||||
while(<>){
 | 
			
		||||
	if($line =~ /^(.*)\\$/) {
 | 
			
		||||
		# Handle continuation
 | 
			
		||||
		$line = $1;
 | 
			
		||||
		$_ =~ s/^\s+//;
 | 
			
		||||
		$line .= $_;
 | 
			
		||||
	} else {
 | 
			
		||||
		# New line
 | 
			
		||||
		$line = $_;
 | 
			
		||||
	}
 | 
			
		||||
	next if $line =~ /\\$/;
 | 
			
		||||
	if($line =~ /^([0-9]+)\s+((STD)|(NOERR))\s+(RUMP\s+)?({\s+\S+\s*\*?\s*\|(\S+)\|(\S*)\|(\w+).*\s+})(\s+(\S+))?$/) {
 | 
			
		||||
		my $num = $1;
 | 
			
		||||
		my $proto = $6;
 | 
			
		||||
		my $compat = $8;
 | 
			
		||||
		my $name = "$7_$9";
 | 
			
		||||
 | 
			
		||||
		$name = "$7_$11" if $11 ne '';
 | 
			
		||||
		$name =~ y/a-z/A-Z/;
 | 
			
		||||
 | 
			
		||||
		if($compat eq '' || $compat eq '30' || $compat eq '50') {
 | 
			
		||||
			print "	$name = $num;  // $proto\n";
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
print <<EOF;
 | 
			
		||||
)
 | 
			
		||||
EOF
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
#!/usr/bin/env perl
 | 
			
		||||
# Copyright 2009 The Go Authors. All rights reserved.
 | 
			
		||||
# Use of this source code is governed by a BSD-style
 | 
			
		||||
# license that can be found in the LICENSE file.
 | 
			
		||||
#
 | 
			
		||||
# Generate system call table for OpenBSD from master list
 | 
			
		||||
# (for example, /usr/src/sys/kern/syscalls.master).
 | 
			
		||||
 | 
			
		||||
use strict;
 | 
			
		||||
 | 
			
		||||
if($ENV{'GOARCH'} eq "" || $ENV{'GOOS'} eq "") {
 | 
			
		||||
	print STDERR "GOARCH or GOOS not defined in environment\n";
 | 
			
		||||
	exit 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
my $command = "mksysnum_openbsd.pl " . join(' ', @ARGV);
 | 
			
		||||
 | 
			
		||||
print <<EOF;
 | 
			
		||||
// $command
 | 
			
		||||
// Code generated by the command above; see README.md. DO NOT EDIT.
 | 
			
		||||
 | 
			
		||||
// +build $ENV{'GOARCH'},$ENV{'GOOS'}
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
EOF
 | 
			
		||||
 | 
			
		||||
while(<>){
 | 
			
		||||
	if(/^([0-9]+)\s+STD\s+(NOLOCK\s+)?({ \S+\s+\*?(\w+).*)$/){
 | 
			
		||||
		my $num = $1;
 | 
			
		||||
		my $proto = $3;
 | 
			
		||||
		my $name = $4;
 | 
			
		||||
		$name =~ y/a-z/A-Z/;
 | 
			
		||||
 | 
			
		||||
		# There are multiple entries for enosys and nosys, so comment them out.
 | 
			
		||||
		if($name =~ /^SYS_E?NOSYS$/){
 | 
			
		||||
			$name = "// $name";
 | 
			
		||||
		}
 | 
			
		||||
		if($name eq 'SYS_SYS_EXIT'){
 | 
			
		||||
			$name = 'SYS_EXIT';
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		print "	$name = $num;  // $proto\n";
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
print <<EOF;
 | 
			
		||||
)
 | 
			
		||||
EOF
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,38 @@
 | 
			
		|||
// Copyright 2016 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build openbsd
 | 
			
		||||
// +build 386 amd64 arm
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	SYS_PLEDGE = 108
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Pledge implements the pledge syscall. For more information see pledge(2).
 | 
			
		||||
func Pledge(promises string, paths []string) error {
 | 
			
		||||
	promisesPtr, err := syscall.BytePtrFromString(promises)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	promisesUnsafe, pathsUnsafe := unsafe.Pointer(promisesPtr), unsafe.Pointer(nil)
 | 
			
		||||
	if paths != nil {
 | 
			
		||||
		var pathsPtr []*byte
 | 
			
		||||
		if pathsPtr, err = syscall.SlicePtrFromStrings(paths); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		pathsUnsafe = unsafe.Pointer(&pathsPtr[0])
 | 
			
		||||
	}
 | 
			
		||||
	_, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(promisesUnsafe), uintptr(pathsUnsafe), 0)
 | 
			
		||||
	if e != 0 {
 | 
			
		||||
		return e
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
// Copyright 2012 The Go Authors.  All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build darwin,race linux,race freebsd,race
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const raceenabled = true
 | 
			
		||||
 | 
			
		||||
func raceAcquire(addr unsafe.Pointer) {
 | 
			
		||||
	runtime.RaceAcquire(addr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func raceReleaseMerge(addr unsafe.Pointer) {
 | 
			
		||||
	runtime.RaceReleaseMerge(addr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func raceReadRange(addr unsafe.Pointer, len int) {
 | 
			
		||||
	runtime.RaceReadRange(addr, len)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func raceWriteRange(addr unsafe.Pointer, len int) {
 | 
			
		||||
	runtime.RaceWriteRange(addr, len)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,25 @@
 | 
			
		|||
// Copyright 2012 The Go Authors.  All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build darwin,!race linux,!race freebsd,!race netbsd openbsd solaris dragonfly
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"unsafe"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const raceenabled = false
 | 
			
		||||
 | 
			
		||||
func raceAcquire(addr unsafe.Pointer) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func raceReleaseMerge(addr unsafe.Pointer) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func raceReadRange(addr unsafe.Pointer, len int) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func raceWriteRange(addr unsafe.Pointer, len int) {
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,36 @@
 | 
			
		|||
// Copyright 2011 The Go Authors.  All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// Socket control messages
 | 
			
		||||
 | 
			
		||||
package unix
 | 
			
		||||
 | 
			
		||||
import "unsafe"
 | 
			
		||||
 | 
			
		||||
// UnixCredentials encodes credentials into a socket control message
 | 
			
		||||
// for sending to another process. This can be used for
 | 
			
		||||
// authentication.
 | 
			
		||||
func UnixCredentials(ucred *Ucred) []byte {
 | 
			
		||||
	b := make([]byte, CmsgSpace(SizeofUcred))
 | 
			
		||||
	h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
 | 
			
		||||
	h.Level = SOL_SOCKET
 | 
			
		||||
	h.Type = SCM_CREDENTIALS
 | 
			
		||||
	h.SetLen(CmsgLen(SizeofUcred))
 | 
			
		||||
	*((*Ucred)(cmsgData(h))) = *ucred
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseUnixCredentials decodes a socket control message that contains
 | 
			
		||||
// credentials in a Ucred structure. To receive such a message, the
 | 
			
		||||
// SO_PASSCRED option must be enabled on the socket.
 | 
			
		||||
func ParseUnixCredentials(m *SocketControlMessage) (*Ucred, error) {
 | 
			
		||||
	if m.Header.Level != SOL_SOCKET {
 | 
			
		||||
		return nil, EINVAL
 | 
			
		||||
	}
 | 
			
		||||
	if m.Header.Type != SCM_CREDENTIALS {
 | 
			
		||||
		return nil, EINVAL
 | 
			
		||||
	}
 | 
			
		||||
	ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0]))
 | 
			
		||||
	return &ucred, nil
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue