1/*
2* Copyright (C) 2011 The Android Open Source Project
3*
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at
7*
8* http://www.apache.org/licenses/LICENSE-2.0
9*
10* Unless required by applicable law or agreed to in writing, software
11* distributed under the License is distributed on an "AS IS" BASIS,
12* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13* See the License for the specific language governing permissions and
14* limitations under the License.
15*/
16#include "UnixStream.h"
17#include <cutils/sockets.h>
18#include <errno.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <unistd.h>
22#include <string.h>
23
24#include <netinet/in.h>
25#include <netinet/tcp.h>
26#include <sys/un.h>
27#include <sys/stat.h>
28
29/* Not all systems define PATH_MAX, those who don't generally don't
30 * have a limit on the maximum path size, so use a value that is
31 * large enough for our very limited needs.
32 */
33#ifndef PATH_MAX
34#define PATH_MAX   128
35#endif
36
37UnixStream::UnixStream(size_t bufSize) :
38    SocketStream(bufSize)
39{
40}
41
42UnixStream::UnixStream(int sock, size_t bufSize) :
43    SocketStream(sock, bufSize)
44{
45}
46
47/* Initialize a sockaddr_un with the appropriate values corresponding
48 * to a given 'virtual port'. Returns 0 on success, -1 on error.
49 */
50static int
51make_unix_path(char *path, size_t  pathlen, int port_number)
52{
53    char  tmp[PATH_MAX];  // temp directory
54    int   ret = 0;
55
56    // First, create user-specific temp directory if needed
57    const char* user = getenv("USER");
58    if (user != NULL) {
59        struct stat  st;
60        snprintf(tmp, sizeof(tmp), "/tmp/android-%s", user);
61        do {
62            ret = ::lstat(tmp, &st);
63        } while (ret < 0 && errno == EINTR);
64
65        if (ret < 0 && errno == ENOENT) {
66            do {
67                ret = ::mkdir(tmp, 0766);
68            } while (ret < 0 && errno == EINTR);
69            if (ret < 0) {
70                ERR("Could not create temp directory: %s", tmp);
71                user = NULL;  // will fall-back to /tmp
72            }
73        }
74        else if (ret < 0) {
75            user = NULL;  // will fallback to /tmp
76        }
77    }
78
79    if (user == NULL) {  // fallback to /tmp in case of error
80        snprintf(tmp, sizeof(tmp), "/tmp");
81    }
82
83    // Now, initialize it properly
84    snprintf(path, pathlen, "%s/qemu-gles-%d", tmp, port_number);
85    return 0;
86}
87
88
89int UnixStream::listen(unsigned short port)
90{
91    char  path[PATH_MAX];
92
93    if (make_unix_path(path, sizeof(path), port) < 0) {
94        return -1;
95    }
96
97    m_sock = socket_local_server(path, ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
98    if (!valid()) return int(ERR_INVALID_SOCKET);
99
100    return 0;
101}
102
103SocketStream * UnixStream::accept()
104{
105    int clientSock = -1;
106
107    while (true) {
108        struct sockaddr_un addr;
109        socklen_t len = sizeof(addr);
110        clientSock = ::accept(m_sock, (sockaddr *)&addr, &len);
111
112        if (clientSock < 0 && errno == EINTR) {
113            continue;
114        }
115        break;
116    }
117
118    UnixStream *clientStream = NULL;
119
120    if (clientSock >= 0) {
121        clientStream =  new UnixStream(clientSock, m_bufsize);
122    }
123    return clientStream;
124}
125
126int UnixStream::connect(unsigned short port)
127{
128    char  path[PATH_MAX];
129
130    if (make_unix_path(path, sizeof(path), port) < 0)
131        return -1;
132
133    m_sock = socket_local_client(path, ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
134    if (!valid()) return -1;
135
136    return 0;
137}
138