Synchronize file rotation

Create a lock file during log rotation so that multiple processes won't
step on each other.

Test: Run `while true; do get_build_var TARGET_PRODUCT; done` in parallel
Test: m blueprint_tools
Change-Id: I7144cd42aca47c694487ddae44713f82665ed81e
diff --git a/ui/logger/logger.go b/ui/logger/logger.go
index 7d9687b..15c413d 100644
--- a/ui/logger/logger.go
+++ b/ui/logger/logger.go
@@ -38,6 +38,7 @@
 	"path/filepath"
 	"strconv"
 	"sync"
+	"syscall"
 )
 
 type Logger interface {
@@ -93,10 +94,19 @@
 // existing files to <filename>.#.<ext>, keeping up to maxCount files.
 // <filename>.1.<ext> is the most recent backup, <filename>.2.<ext> is the
 // second most recent backup, etc.
-//
-// TODO: This function is not guaranteed to be atomic, if there are multiple
-// users attempting to do the same operation, the result is undefined.
 func CreateFileWithRotation(filename string, maxCount int) (*os.File, error) {
+	lockFileName := filepath.Join(filepath.Dir(filename), ".lock_"+filepath.Base(filename))
+	lockFile, err := os.OpenFile(lockFileName, os.O_RDWR|os.O_CREATE, 0666)
+	if err != nil {
+		return nil, err
+	}
+	defer lockFile.Close()
+
+	err = syscall.Flock(int(lockFile.Fd()), syscall.LOCK_EX)
+	if err != nil {
+		return nil, err
+	}
+
 	if _, err := os.Lstat(filename); err == nil {
 		ext := filepath.Ext(filename)
 		basename := filename[:len(filename)-len(ext)]
diff --git a/ui/logger/logger_test.go b/ui/logger/logger_test.go
index 0f88ab3..dc6f2e9 100644
--- a/ui/logger/logger_test.go
+++ b/ui/logger/logger_test.go
@@ -67,7 +67,7 @@
 		t.Fatalf("Failed to read dir: %v", err)
 	}
 	sort.Strings(names)
-	expected := []string{"build.1.log", "build.2.log", "build.3.log", "build.log"}
+	expected := []string{".lock_build.log", "build.1.log", "build.2.log", "build.3.log", "build.log"}
 	if !reflect.DeepEqual(names, expected) {
 		t.Errorf("File list does not match.")
 		t.Errorf("     got: %v", names)